[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / music / dialogs / GUIDialogSongInfo.cpp
blobc0101d0ff37234b5f99a8d2b54364e46024f6933
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 "GUIDialogSongInfo.h"
11 #include "GUIDialogMusicInfo.h"
12 #include "GUIPassword.h"
13 #include "GUIUserMessages.h"
14 #include "ServiceBroker.h"
15 #include "TextureCache.h"
16 #include "Util.h"
17 #include "dialogs/GUIDialogBusy.h"
18 #include "dialogs/GUIDialogFileBrowser.h"
19 #include "guilib/GUIComponent.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "guilib/LocalizeStrings.h"
22 #include "input/actions/Action.h"
23 #include "input/actions/ActionIDs.h"
24 #include "music/MusicDatabase.h"
25 #include "music/MusicFileItemClassify.h"
26 #include "music/MusicUtils.h"
27 #include "music/tags/MusicInfoTag.h"
28 #include "music/windows/GUIWindowMusicBase.h"
29 #include "profiles/ProfileManager.h"
30 #include "settings/MediaSourceSettings.h"
31 #include "settings/SettingsComponent.h"
32 #include "storage/MediaManager.h"
33 #include "utils/FileUtils.h"
35 using namespace KODI;
37 #define CONTROL_BTN_REFRESH 6
38 #define CONTROL_USERRATING 7
39 #define CONTROL_BTN_PLAY 8
40 #define CONTROL_BTN_GET_THUMB 10
41 #define CONTROL_ALBUMINFO 12
43 #define CONTROL_LIST 50
45 #define TIME_TO_BUSY_DIALOG 500
47 class CGetSongInfoJob : public CJob
49 public:
50 ~CGetSongInfoJob(void) override = default;
52 // Fetch full song information including art types list
53 bool DoWork() override
55 CGUIDialogSongInfo *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSongInfo>(WINDOW_DIALOG_SONG_INFO);
56 if (!dialog)
57 return false;
58 if (dialog->IsCancelled())
59 return false;
60 CFileItemPtr m_song = dialog->GetCurrentListItem();
62 // Fetch tag data from library using filename of item path, or scanning file
63 // (if item does not already have this loaded)
64 if (!m_song->LoadMusicTag())
66 // Stop SongInfoDialog waiting
67 dialog->FetchComplete();
68 return false;
70 if (dialog->IsCancelled())
71 return false;
72 // Fetch album and primary song artist data from library as properties
73 // and lyrics by scanning tags from file
74 MUSIC_INFO::CMusicInfoLoader::LoadAdditionalTagInfo(m_song.get());
75 if (dialog->IsCancelled())
76 return false;
78 // Get album path (for use in browsing art selection)
79 std::string albumpath;
80 CMusicDatabase db;
81 db.Open();
82 db.GetAlbumPath(m_song->GetMusicInfoTag()->GetAlbumId(), albumpath);
83 m_song->SetProperty("album_path", albumpath);
84 db.Close();
85 if (dialog->IsCancelled())
86 return false;
88 // Load song art.
89 // For songs in library this includes related album and artist(s) art.
90 // Also fetches artist art for non library songs when artist can be found
91 // uniquely by name, otherwise just embedded or cached thumb is fetched.
92 CMusicThumbLoader loader;
93 loader.LoadItem(m_song.get());
94 if (dialog->IsCancelled())
95 return false;
97 // For songs in library fill vector of possible art types, with current art when it exists
98 // for display on the art type selection dialog
99 CFileItemList artlist;
100 MUSIC_UTILS::FillArtTypesList(*m_song, artlist);
101 dialog->SetArtTypeList(artlist);
102 if (dialog->IsCancelled())
103 return false;
105 // Tell waiting SongInfoDialog that job is complete
106 dialog->FetchComplete();
108 return true;
112 CGUIDialogSongInfo::CGUIDialogSongInfo(void)
113 : CGUIDialog(WINDOW_DIALOG_SONG_INFO, "DialogMusicInfo.xml")
114 , m_song(new CFileItem)
116 m_cancelled = false;
117 m_hasUpdatedUserrating = false;
118 m_startUserrating = -1;
119 m_artTypeList.Clear();
120 m_loadType = KEEP_IN_MEMORY;
123 CGUIDialogSongInfo::~CGUIDialogSongInfo(void) = default;
125 bool CGUIDialogSongInfo::OnMessage(CGUIMessage& message)
127 switch (message.GetMessage())
129 case GUI_MSG_WINDOW_DEINIT:
131 m_artTypeList.Clear();
132 if (m_startUserrating != m_song->GetMusicInfoTag()->GetUserrating())
134 m_hasUpdatedUserrating = true;
136 // Asynchronously update song userrating in library
137 MUSIC_UTILS::UpdateSongRatingJob(m_song, m_song->GetMusicInfoTag()->GetUserrating());
139 // Send a message to all windows to tell them to update the fileitem
140 // This communicates the rating change to the music lib window, current playlist and OSD.
141 // The music lib window item is updated to but changes to the rating when it is the sort
142 // do not show on screen until refresh() that fetches the list from scratch, sorts etc.
143 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song);
144 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
146 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
147 OnMessage(msg);
148 break;
150 case GUI_MSG_WINDOW_INIT:
151 CGUIDialog::OnMessage(message);
152 Update();
153 m_cancelled = false;
154 break;
156 case GUI_MSG_CLICKED:
158 int iControl = message.GetSenderId();
159 if (iControl == CONTROL_USERRATING)
161 OnSetUserrating();
163 else if (iControl == CONTROL_ALBUMINFO)
165 CGUIDialogMusicInfo::ShowForAlbum(m_albumId);
166 return true;
168 else if (iControl == CONTROL_BTN_GET_THUMB)
170 OnGetArt();
171 return true;
173 else if (iControl == CONTROL_LIST)
175 int iAction = message.GetParam1();
176 if ((ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction))
178 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
179 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
180 int iItem = msg.GetParam1();
181 if (iItem < 0 || iItem >= static_cast<int>(m_song->GetMusicInfoTag()->GetContributors().size()))
182 break;
183 int idArtist = m_song->GetMusicInfoTag()->GetContributors()[iItem].GetArtistId();
184 if (idArtist > 0)
185 CGUIDialogMusicInfo::ShowForArtist(idArtist);
186 return true;
189 else if (iControl == CONTROL_BTN_PLAY)
191 OnPlaySong(m_song);
192 return true;
194 return false;
196 break;
199 return CGUIDialog::OnMessage(message);
202 bool CGUIDialogSongInfo::OnAction(const CAction& action)
204 int userrating = m_song->GetMusicInfoTag()->GetUserrating();
205 if (action.GetID() == ACTION_INCREASE_RATING)
207 SetUserrating(userrating + 1);
208 return true;
210 else if (action.GetID() == ACTION_DECREASE_RATING)
212 SetUserrating(userrating - 1);
213 return true;
215 else if (action.GetID() == ACTION_SHOW_INFO)
217 Close();
218 return true;
220 return CGUIDialog::OnAction(action);
223 bool CGUIDialogSongInfo::OnBack(int actionID)
225 m_cancelled = true;
226 return CGUIDialog::OnBack(actionID);
229 void CGUIDialogSongInfo::FetchComplete()
231 //Trigger the event to indicate data has been fetched
232 m_event.Set();
235 void CGUIDialogSongInfo::OnInitWindow()
237 // Enable album info button when we know album
238 m_albumId = m_song->GetMusicInfoTag()->GetAlbumId();
240 CONTROL_ENABLE_ON_CONDITION(CONTROL_ALBUMINFO, m_albumId > 0);
242 // Disable music user rating button for plugins as they don't have tables to save this
243 if (m_song->IsPlugin())
244 CONTROL_DISABLE(CONTROL_USERRATING);
245 else
246 CONTROL_ENABLE(CONTROL_USERRATING);
248 // Disable the Choose Art button if the user isn't allowed it
249 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
250 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB,
251 profileManager->GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser);
253 SET_CONTROL_HIDDEN(CONTROL_BTN_REFRESH);
254 SET_CONTROL_LABEL(CONTROL_USERRATING, 38023);
255 SET_CONTROL_LABEL(CONTROL_BTN_GET_THUMB, 13511);
256 SET_CONTROL_LABEL(CONTROL_ALBUMINFO, 10523);
257 SET_CONTROL_LABEL(CONTROL_BTN_PLAY, 208);
259 CGUIDialog::OnInitWindow();
262 void CGUIDialogSongInfo::Update()
264 CFileItemList items;
265 for (const auto& contributor : m_song->GetMusicInfoTag()->GetContributors())
267 auto item = std::make_shared<CFileItem>(contributor.GetRoleDesc());
268 item->SetLabel2(contributor.GetArtist());
269 item->GetMusicInfoTag()->SetDatabaseId(contributor.GetArtistId(), MediaTypeArtist);
270 items.Add(std::move(item));
272 CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, &items);
273 OnMessage(message);
276 void CGUIDialogSongInfo::SetUserrating(int userrating)
278 userrating = std::max(userrating, 0);
279 userrating = std::min(userrating, 10);
280 if (userrating != m_song->GetMusicInfoTag()->GetUserrating())
282 m_song->GetMusicInfoTag()->SetUserrating(userrating);
286 bool CGUIDialogSongInfo::SetSong(CFileItem* item)
288 *m_song = *item;
289 m_event.Reset();
290 m_cancelled = false; // SetSong happens before win_init
291 // In a separate job fetch song info and fill list of art types.
292 int jobid =
293 CServiceBroker::GetJobManager()->AddJob(new CGetSongInfoJob(), nullptr, CJob::PRIORITY_LOW);
295 // Wait to get all data before show, allowing user to cancel if fetch is slow
296 if (!CGUIDialogBusy::WaitOnEvent(m_event, TIME_TO_BUSY_DIALOG))
298 // Cancel job still waiting in queue (unlikely)
299 CServiceBroker::GetJobManager()->CancelJob(jobid);
300 // Flag to stop job already in progress
301 m_cancelled = true;
302 return false;
305 // Store initial userrating
306 m_startUserrating = m_song->GetMusicInfoTag()->GetUserrating();
307 m_hasUpdatedUserrating = false;
308 return true;
311 void CGUIDialogSongInfo::SetArtTypeList(CFileItemList& artlist)
313 m_artTypeList.Copy(artlist);
316 CFileItemPtr CGUIDialogSongInfo::GetCurrentListItem(int offset)
318 return m_song;
321 std::string CGUIDialogSongInfo::GetContent()
323 return "songs";
327 Allow user to choose artwork for the song
328 For each type of art the options are:
329 1. Current art
330 2. Local art (thumb found by filename)
331 3. Embedded art (@todo)
332 4. None
333 Note that songs are not scraped, hence there is no list of urls for possible remote art
335 void CGUIDialogSongInfo::OnGetArt()
337 std::string type = MUSIC_UTILS::ShowSelectArtTypeDialog(m_artTypeList);
338 if (type.empty())
339 return; // Cancelled
341 CFileItemList items;
342 CGUIListItem::ArtMap primeArt = m_song->GetArt(); // Song art without fallbacks
343 bool bHasArt = m_song->HasArt(type);
344 bool bFallback(false);
345 if (bHasArt)
347 // Check if that type of art is actually a fallback, e.g. album thumb or artist fanart
348 CGUIListItem::ArtMap::const_iterator i = primeArt.find(type);
349 bFallback = (i == primeArt.end());
352 // Build list of possible images of that art type
353 if (bHasArt)
355 // Add item for current artwork, could a fallback from album/artist
356 CFileItemPtr item(new CFileItem("thumb://Current", false));
357 item->SetArt("thumb", m_song->GetArt(type));
358 item->SetArt("icon", "DefaultPicture.png");
359 item->SetLabel(g_localizeStrings.Get(13512)); //! @todo: label fallback art so user knows?
360 items.Add(item);
362 else if (m_song->HasArt("thumb"))
363 { // For missing art of that type add the thumb (when it exists and not a fallback)
364 CGUIListItem::ArtMap::const_iterator i = primeArt.find("thumb");
365 if (i != primeArt.end())
367 CFileItemPtr item(new CFileItem("thumb://Thumb", false));
368 item->SetArt("thumb", m_song->GetArt("thumb"));
369 item->SetArt("icon", "DefaultAlbumCover.png");
370 item->SetLabel(g_localizeStrings.Get(21371));
371 items.Add(item);
375 std::string localThumb;
376 if (type == "thumb")
377 { // Local thumb type art held in <filename>.tbn (for non-library items)
378 localThumb = m_song->GetUserMusicThumb(true);
379 if (MUSIC::IsMusicDb(*m_song))
381 CFileItem item(m_song->GetMusicInfoTag()->GetURL(), false);
382 localThumb = item.GetUserMusicThumb(true);
384 if (CFileUtils::Exists(localThumb))
386 CFileItemPtr item(new CFileItem("thumb://Local", false));
387 item->SetArt("thumb", localThumb);
388 item->SetLabel(g_localizeStrings.Get(20017));
389 items.Add(item);
393 // Clear these local images from cache so user will see any recent
394 // local file changes immediately
395 for (auto& item : items)
397 std::string thumb(item->GetArt("thumb"));
398 if (thumb.empty())
399 continue;
400 CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
401 // Remove any thumbnail of local image too (created when browsing files)
402 std::string thumbthumb(CTextureUtils::GetWrappedThumbURL(thumb));
403 CServiceBroker::GetTextureCache()->ClearCachedImage(thumbthumb);
406 if (bHasArt && !bFallback)
407 { // Actually has this type of art (not a fallback) so
408 // allow the user to delete it by selecting "no art".
409 CFileItemPtr item(new CFileItem("thumb://None", false));
410 item->SetArt("thumb", "DefaultAlbumCover.png");
411 item->SetLabel(g_localizeStrings.Get(13515));
412 items.Add(item);
415 //! @todo: Add support for extracting embedded art
417 // Show list of possible art for user selection
418 std::string result;
419 VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("music"));
420 // Add album folder as source (could be disc set)
421 std::string albumpath = m_song->GetProperty("album_path").asString();
422 if (!albumpath.empty())
424 CFileItem pathItem(albumpath, true);
425 CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, pathItem);
427 else // Add parent folder of song
428 CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, *m_song);
429 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
430 if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
431 result != "thumb://Current")
433 // User didn't choose the one they have, or the fallback image.
434 // Overwrite with the new art or clear it
435 std::string newArt;
436 if (result == "thumb://Thumb")
437 newArt = m_song->GetArt("thumb");
438 else if (result == "thumb://Local")
439 newArt = localThumb;
440 // else if (result == "thumb://Embedded")
441 // newArt = embeddedArt;
442 else if (CFileUtils::Exists(result))
443 newArt = result;
444 else // none
445 newArt.clear();
447 // Asynchronously update that type of art in the database
448 MUSIC_UTILS::UpdateArtJob(m_song, type, newArt);
450 // Update local song with current art
451 if (newArt.empty())
453 // Remove that type of art from the song
454 primeArt.erase(type);
455 m_song->SetArt(primeArt);
457 else
458 // Add or modify the type of art
459 m_song->SetArt(type, newArt);
461 // Update local art list with current art
462 // Show any fallback art when song art removed
463 if (newArt.empty() && m_song->HasArt(type))
464 newArt = m_song->GetArt(type);
465 for (const auto& artitem : m_artTypeList)
467 if (artitem->GetProperty("artType") == type)
469 artitem->SetArt("thumb", newArt);
470 break;
474 // Get new artwork to show in other places e.g. on music lib window,
475 // current playlist and player OSD.
476 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song);
477 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
481 // Re-open the art type selection dialog as we come back from
482 // the image selection dialog
483 OnGetArt();
486 void CGUIDialogSongInfo::OnSetUserrating()
488 int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_song->GetMusicInfoTag()->GetUserrating());
489 if (userrating < 0) // Nothing selected, so rating unchanged
490 return;
492 SetUserrating(userrating);
495 void CGUIDialogSongInfo::ShowFor(CFileItem* pItem)
497 if (pItem->m_bIsFolder)
498 return;
499 if (!MUSIC::IsMusicDb(*pItem))
500 pItem->LoadMusicTag();
501 if (!pItem->HasMusicInfoTag())
502 return;
504 CGUIDialogSongInfo *dialog = CServiceBroker::GetGUI()->GetWindowManager().
505 GetWindow<CGUIDialogSongInfo>(WINDOW_DIALOG_SONG_INFO);
506 if (dialog)
508 if (dialog->SetSong(pItem)) // Fetch full song info asynchronously
510 dialog->Open();
511 if (dialog->HasUpdatedUserrating())
513 auto window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowMusicBase>(WINDOW_MUSIC_NAV);
514 if (window)
515 window->RefreshContent("songs");
521 void CGUIDialogSongInfo::OnPlaySong(const std::shared_ptr<CFileItem>& item)
523 Close(true);
524 MUSIC_UTILS::PlayItem(item, "");