[musicdb] fix: make the guidialog discography use artist id instead of name
[vuplus_xbmc] / xbmc / music / dialogs / GUIDialogMusicInfo.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "GUIDialogMusicInfo.h"
22 #include "guilib/GUIWindowManager.h"
23 #include "guilib/GUIImage.h"
24 #include "dialogs/GUIDialogFileBrowser.h"
25 #include "GUIPassword.h"
26 #include "music/MusicDatabase.h"
27 #include "music/tags/MusicInfoTag.h"
28 #include "URL.h"
29 #include "filesystem/File.h"
30 #include "FileItem.h"
31 #include "profiles/ProfilesManager.h"
32 #include "storage/MediaManager.h"
33 #include "utils/AsyncFileCopy.h"
34 #include "settings/AdvancedSettings.h"
35 #include "settings/MediaSourceSettings.h"
36 #include "settings/Settings.h"
37 #include "guilib/Key.h"
38 #include "guilib/LocalizeStrings.h"
39 #include "utils/log.h"
40 #include "utils/URIUtils.h"
41 #include "utils/StringUtils.h"
42 #include "TextureCache.h"
43 #include "music/MusicThumbLoader.h"
44 #include "filesystem/Directory.h"
45
46 using namespace std;
47 using namespace XFILE;
48
49 #define CONTROL_IMAGE            3
50 #define CONTROL_TEXTAREA         4
51
52 #define CONTROL_BTN_TRACKS       5
53 #define CONTROL_BTN_REFRESH      6
54 #define CONTROL_BTN_GET_THUMB   10
55 #define  CONTROL_BTN_GET_FANART 12
56
57 #define CONTROL_LIST            50
58
59 CGUIDialogMusicInfo::CGUIDialogMusicInfo(void)
60     : CGUIDialog(WINDOW_DIALOG_MUSIC_INFO, "DialogAlbumInfo.xml")
61     , m_albumItem(new CFileItem)
62 {
63   m_bRefresh = false;
64   m_albumSongs = new CFileItemList;
65   m_loadType = KEEP_IN_MEMORY;
66 }
67
68 CGUIDialogMusicInfo::~CGUIDialogMusicInfo(void)
69 {
70   delete m_albumSongs;
71 }
72
73 bool CGUIDialogMusicInfo::OnMessage(CGUIMessage& message)
74 {
75   switch ( message.GetMessage() )
76   {
77   case GUI_MSG_WINDOW_DEINIT:
78     {
79       CGUIMessage message(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
80       OnMessage(message);
81       m_albumSongs->Clear();
82     }
83     break;
84
85   case GUI_MSG_WINDOW_INIT:
86     {
87       CGUIDialog::OnMessage(message);
88       m_bViewReview = true;
89       m_bRefresh = false;
90       Update();
91       return true;
92     }
93     break;
94
95
96   case GUI_MSG_CLICKED:
97     {
98       int iControl = message.GetSenderId();
99       if (iControl == CONTROL_BTN_REFRESH)
100       {
101         m_bRefresh = true;
102         Close();
103         return true;
104       }
105       else if (iControl == CONTROL_BTN_GET_THUMB)
106       {
107         OnGetThumb();
108       }
109       else if (iControl == CONTROL_BTN_TRACKS)
110       {
111         m_bViewReview = !m_bViewReview;
112         Update();
113       }
114       else if (iControl == CONTROL_LIST)
115       {
116         int iAction = message.GetParam1();
117         if (m_bArtistInfo && (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction))
118         {
119           CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
120           g_windowManager.SendMessage(msg);
121           int iItem = msg.GetParam1();
122           if (iItem < 0 || iItem >= (int)m_albumSongs->Size())
123             break;
124           CFileItemPtr item = m_albumSongs->Get(iItem);
125           OnSearch(item.get());
126           return true;
127         }
128       }
129       else if (iControl == CONTROL_BTN_GET_FANART)
130       {
131         OnGetFanart();
132       }
133     }
134     break;
135   }
136
137   return CGUIDialog::OnMessage(message);
138 }
139
140 bool CGUIDialogMusicInfo::OnAction(const CAction &action)
141 {
142   if (action.GetID() == ACTION_SHOW_INFO)
143   {
144     Close();
145     return true;
146   }
147   return CGUIDialog::OnAction(action);
148 }
149
150 void CGUIDialogMusicInfo::SetAlbum(const CAlbum& album, const CStdString &path)
151 {
152   m_album = album;
153   SetSongs(m_album.songs);
154   *m_albumItem = CFileItem(path, true);
155   m_albumItem->GetMusicInfoTag()->SetAlbum(m_album.strAlbum);
156   m_albumItem->GetMusicInfoTag()->SetAlbumArtist(StringUtils::Join(m_album.artist, g_advancedSettings.m_musicItemSeparator));
157   m_albumItem->GetMusicInfoTag()->SetArtist(m_album.artist);
158   m_albumItem->GetMusicInfoTag()->SetYear(m_album.iYear);
159   m_albumItem->GetMusicInfoTag()->SetLoaded(true);
160   m_albumItem->GetMusicInfoTag()->SetRating('0' + m_album.iRating);
161   m_albumItem->GetMusicInfoTag()->SetGenre(m_album.genre);
162   m_albumItem->GetMusicInfoTag()->SetDatabaseId(m_album.idAlbum, "album");
163   CMusicDatabase::SetPropertiesFromAlbum(*m_albumItem,m_album);
164
165   CMusicThumbLoader loader;
166   loader.LoadItem(m_albumItem.get());
167
168   // set the artist thumb, fanart
169   if (!m_album.artist.empty())
170   {
171     CMusicDatabase db;
172     db.Open();
173     map<string, string> artwork;
174     if (db.GetArtistArtForItem(m_album.idAlbum, "album", artwork))
175     {
176       if (artwork.find("thumb") != artwork.end())
177         m_albumItem->SetProperty("artistthumb", artwork["thumb"]);
178       if (artwork.find("fanart") != artwork.end())
179         m_albumItem->SetArt("fanart",artwork["fanart"]);
180     }
181   }
182   m_hasUpdatedThumb = false;
183   m_bArtistInfo = false;
184   m_albumSongs->SetContent("albums");
185 }
186
187 void CGUIDialogMusicInfo::SetArtist(const CArtist& artist, const CStdString &path)
188 {
189   m_artist = artist;
190   SetDiscography();
191   *m_albumItem = CFileItem(path, true);
192   m_albumItem->SetLabel(artist.strArtist);
193   m_albumItem->GetMusicInfoTag()->SetAlbumArtist(m_artist.strArtist);
194   m_albumItem->GetMusicInfoTag()->SetArtist(m_artist.strArtist);
195   m_albumItem->GetMusicInfoTag()->SetLoaded(true);
196   m_albumItem->GetMusicInfoTag()->SetGenre(m_artist.genre);
197   m_albumItem->GetMusicInfoTag()->SetDatabaseId(m_artist.idArtist, "artist");
198   CMusicDatabase::SetPropertiesFromArtist(*m_albumItem,m_artist);
199
200   CMusicThumbLoader loader;
201   loader.LoadItem(m_albumItem.get());
202
203   m_hasUpdatedThumb = false;
204   m_bArtistInfo = true;
205   m_albumSongs->SetContent("artists");
206 }
207
208 void CGUIDialogMusicInfo::SetSongs(const VECSONGS &songs)
209 {
210   m_albumSongs->Clear();
211   for (unsigned int i = 0; i < songs.size(); i++)
212   {
213     const CSong& song = songs[i];
214     CFileItemPtr item(new CFileItem(song));
215     m_albumSongs->Add(item);
216   }
217 }
218
219 void CGUIDialogMusicInfo::SetDiscography()
220 {
221   m_albumSongs->Clear();
222   CMusicDatabase database;
223   database.Open();
224
225   vector<int> albumsByArtist;
226   database.GetAlbumsByArtist(m_artist.idArtist, true, albumsByArtist);
227
228   for (unsigned int i=0;i<m_artist.discography.size();++i)
229   {
230     CFileItemPtr item(new CFileItem(m_artist.discography[i].first));
231     item->SetLabel2(m_artist.discography[i].second);
232
233     int idAlbum = -1;
234     for (vector<int>::const_iterator album = albumsByArtist.begin(); album != albumsByArtist.end(); ++album)
235     {
236       if (database.GetAlbumById(*album).Equals(item->GetLabel()))
237       {
238         idAlbum = *album;
239         item->GetMusicInfoTag()->SetDatabaseId(idAlbum, "album");
240         break;
241       }
242     }
243
244     if (idAlbum != -1) // we need this slight stupidity to get correct case for the album name
245       item->SetArt("thumb", database.GetArtForItem(idAlbum, "album", "thumb"));
246     else
247       item->SetArt("thumb", "DefaultAlbumCover.png");
248
249     m_albumSongs->Add(item);
250   }
251 }
252
253 void CGUIDialogMusicInfo::Update()
254 {
255   if (m_bArtistInfo)
256   {
257     CONTROL_ENABLE(CONTROL_BTN_GET_FANART);
258
259     SetLabel(CONTROL_TEXTAREA, m_artist.strBiography);
260     CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_albumSongs);
261     OnMessage(message);
262
263     if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
264     {
265       if (m_bViewReview)
266       {
267         SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
268         SET_CONTROL_HIDDEN(CONTROL_LIST);
269         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 21888);
270       }
271       else
272       {
273         SET_CONTROL_VISIBLE(CONTROL_LIST);
274         SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
275         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 21887);
276       }
277     }
278   }
279   else
280   {
281     CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
282
283     SetLabel(CONTROL_TEXTAREA, m_album.strReview);
284     CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_albumSongs);
285     OnMessage(message);
286
287     if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
288     {
289       if (m_bViewReview)
290       {
291         SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
292         SET_CONTROL_HIDDEN(CONTROL_LIST);
293         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 182);
294       }
295       else
296       {
297         SET_CONTROL_VISIBLE(CONTROL_LIST);
298         SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
299         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 183);
300       }
301     }
302   }
303   // update the thumbnail
304   const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
305   if (pControl)
306   {
307     CGUIImage* pImageControl = (CGUIImage*)pControl;
308     pImageControl->FreeResources();
309     pImageControl->SetFileName(m_albumItem->GetArt("thumb"));
310   }
311
312   // disable the GetThumb button if the user isn't allowed it
313   CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB, CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser);
314 }
315
316 void CGUIDialogMusicInfo::SetLabel(int iControl, const CStdString& strLabel)
317 {
318   if (strLabel.empty())
319   {
320     SET_CONTROL_LABEL(iControl, (iControl == CONTROL_TEXTAREA) ? (m_bArtistInfo?547:414) : 416);
321   }
322   else
323   {
324     SET_CONTROL_LABEL(iControl, strLabel);
325   }
326 }
327
328 bool CGUIDialogMusicInfo::NeedRefresh() const
329 {
330   return m_bRefresh;
331 }
332
333 void CGUIDialogMusicInfo::OnInitWindow()
334 {
335   CGUIDialog::OnInitWindow();
336 }
337
338 // Get Thumb from user choice.
339 // Options are:
340 // 1.  Current thumb
341 // 2.  AllMusic.com thumb
342 // 3.  Local thumb
343 // 4.  No thumb (if no Local thumb is available)
344
345 // TODO: Currently no support for "embedded thumb" as there is no easy way to grab it
346 //       without sending a file that has this as it's album to this class
347 void CGUIDialogMusicInfo::OnGetThumb()
348 {
349   CFileItemList items;
350
351   // Current thumb
352   if (CFile::Exists(m_albumItem->GetArt("thumb")))
353   {
354     CFileItemPtr item(new CFileItem("thumb://Current", false));
355     item->SetArt("thumb", m_albumItem->GetArt("thumb"));
356     item->SetLabel(g_localizeStrings.Get(20016));
357     items.Add(item);
358   }
359
360   // Grab the thumbnail(s) from the web
361   vector<CStdString> thumbs;
362   if (m_bArtistInfo)
363     m_artist.thumbURL.GetThumbURLs(thumbs);
364   else
365     m_album.thumbURL.GetThumbURLs(thumbs);
366
367   for (unsigned int i = 0; i < thumbs.size(); ++i)
368   {
369     CStdString strItemPath;
370     strItemPath = StringUtils::Format("thumb://Remote%i", i);
371     CFileItemPtr item(new CFileItem(strItemPath, false));
372     item->SetArt("thumb", thumbs[i]);
373     item->SetIconImage("DefaultPicture.png");
374     item->SetLabel(g_localizeStrings.Get(20015));
375     
376     // TODO: Do we need to clear the cached image?
377     //    CTextureCache::Get().ClearCachedImage(thumb);
378     items.Add(item);
379   }
380
381   // local thumb
382   CStdString localThumb;
383   if (m_bArtistInfo)
384   {
385     CMusicDatabase database;
386     database.Open();
387     CStdString strArtistPath;
388     if (database.GetArtistPath(m_artist.idArtist,strArtistPath))
389       localThumb = URIUtils::AddFileToFolder(strArtistPath, "folder.jpg");
390   }
391   else
392     localThumb = m_albumItem->GetUserMusicThumb();
393   if (CFile::Exists(localThumb))
394   {
395     CFileItemPtr item(new CFileItem("thumb://Local", false));
396     item->SetArt("thumb", localThumb);
397     item->SetLabel(g_localizeStrings.Get(20017));
398     items.Add(item);
399   }
400   else
401   {
402     CFileItemPtr item(new CFileItem("thumb://None", false));
403     if (m_bArtistInfo)
404       item->SetIconImage("DefaultArtist.png");
405     else
406       item->SetIconImage("DefaultAlbumCover.png");
407     item->SetLabel(g_localizeStrings.Get(20018));
408     items.Add(item);
409   }
410
411   CStdString result;
412   bool flip=false;
413   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("music"));
414   AddItemPathToFileBrowserSources(sources, *m_albumItem);
415   g_mediaManager.GetLocalDrives(sources);
416   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(1030), result, &flip))
417     return;   // user cancelled
418
419   if (result == "thumb://Current")
420     return;   // user chose the one they have
421
422   CStdString newThumb;
423   if (StringUtils::StartsWith(result, "thumb://Remote"))
424   {
425     int number = atoi(result.substr(14).c_str());
426     newThumb = thumbs[number];
427   }
428   else if (result == "thumb://Local")
429     newThumb = localThumb;
430   else if (CFile::Exists(result))
431     newThumb = result;
432   else // none
433     newThumb = "-"; // force local thumbs to be ignored
434
435   // update thumb in the database
436   CMusicDatabase db;
437   if (db.Open())
438   {
439     db.SetArtForItem(m_albumItem->GetMusicInfoTag()->GetDatabaseId(), m_albumItem->GetMusicInfoTag()->GetType(), "thumb", newThumb);
440     db.Close();
441   }
442
443   m_albumItem->SetArt("thumb", newThumb);
444   m_hasUpdatedThumb = true;
445
446   // tell our GUI to completely reload all controls (as some of them
447   // are likely to have had this image in use so will need refreshing)
448   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
449   g_windowManager.SendMessage(msg);
450   // Update our screen
451   Update();
452 }
453
454
455 // Allow user to select a Fanart
456 void CGUIDialogMusicInfo::OnGetFanart()
457 {
458   CFileItemList items;
459
460   if (m_albumItem->HasArt("fanart"))
461   {
462     CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
463     itemCurrent->SetArt("thumb", m_albumItem->GetArt("fanart"));
464     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
465     items.Add(itemCurrent);
466   }
467
468   // Grab the thumbnails from the web
469   for (unsigned int i = 0; i < m_artist.fanart.GetNumFanarts(); i++)
470   {
471     CStdString strItemPath = StringUtils::Format("fanart://Remote%i",i);
472     CFileItemPtr item(new CFileItem(strItemPath, false));
473     CStdString thumb = m_artist.fanart.GetPreviewURL(i);
474     item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
475     item->SetIconImage("DefaultPicture.png");
476     item->SetLabel(g_localizeStrings.Get(20441));
477
478     // TODO: Do we need to clear the cached image?
479     //    CTextureCache::Get().ClearCachedImage(thumb);
480     items.Add(item);
481   }
482
483   // Grab a local thumb
484   CMusicDatabase database;
485   database.Open();
486   CStdString strArtistPath;
487   database.GetArtistPath(m_artist.idArtist,strArtistPath);
488   CFileItem item(strArtistPath,true);
489   CStdString strLocal = item.GetLocalFanart();
490   if (!strLocal.empty())
491   {
492     CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
493     itemLocal->SetArt("thumb", strLocal);
494     itemLocal->SetLabel(g_localizeStrings.Get(20438));
495
496     // TODO: Do we need to clear the cached image?
497     CTextureCache::Get().ClearCachedImage(strLocal);
498     items.Add(itemLocal);
499   }
500   else
501   {
502     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
503     itemNone->SetIconImage("DefaultArtist.png");
504     itemNone->SetLabel(g_localizeStrings.Get(20439));
505     items.Add(itemNone);
506   }
507
508   CStdString result;
509   VECSOURCES sources = *CMediaSourceSettings::Get().GetSources("music");
510   g_mediaManager.GetLocalDrives(sources);
511   bool flip=false;
512   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445))
513     return;   // user cancelled
514
515   // delete the thumbnail if that's what the user wants, else overwrite with the
516   // new thumbnail
517   if (result.Equals("fanart://Current"))
518    return;
519
520   if (result.Equals("fanart://Local"))
521     result = strLocal;
522
523   if (StringUtils::StartsWith(result, "fanart://Remote"))
524   {
525     int iFanart = atoi(result.substr(15).c_str());
526     m_artist.fanart.SetPrimaryFanart(iFanart);
527     result = m_artist.fanart.GetImageURL();
528   }
529   else if (result.Equals("fanart://None") || !CFile::Exists(result))
530     result.clear();
531
532   if (flip && !result.empty())
533     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
534
535   // update thumb in the database
536   CMusicDatabase db;
537   if (db.Open())
538   {
539     db.SetArtForItem(m_albumItem->GetMusicInfoTag()->GetDatabaseId(), m_albumItem->GetMusicInfoTag()->GetType(), "fanart", result);
540     db.Close();
541   }
542
543   m_albumItem->SetArt("fanart", result);
544   m_hasUpdatedThumb = true;
545   // tell our GUI to completely reload all controls (as some of them
546   // are likely to have had this image in use so will need refreshing)
547   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
548   g_windowManager.SendMessage(msg);
549   // Update our screen
550   Update();
551 }
552
553 void CGUIDialogMusicInfo::OnSearch(const CFileItem* pItem)
554 {
555   CMusicDatabase database;
556   database.Open();
557   if (pItem->HasMusicInfoTag() &&
558       pItem->GetMusicInfoTag()->GetDatabaseId() > 0)
559   {
560     CAlbum album;
561     if (database.GetAlbumInfo(pItem->GetMusicInfoTag()->GetDatabaseId(), album, &album.songs))
562     {
563       CStdString strPath;
564       database.GetAlbumPath(pItem->GetMusicInfoTag()->GetDatabaseId(), strPath);
565       SetAlbum(album,strPath);
566       Update();
567     }
568   }
569 }
570
571 CFileItemPtr CGUIDialogMusicInfo::GetCurrentListItem(int offset)
572 {
573   return m_albumItem;
574 }
575
576 void CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
577 {
578   CStdString itemDir;
579
580   if (item.HasMusicInfoTag() && item.GetMusicInfoTag()->GetType() == "song")
581     itemDir = URIUtils::GetParentPath(item.GetMusicInfoTag()->GetURL());
582   else
583     itemDir = item.GetPath();
584
585   if (!itemDir.empty() && CDirectory::Exists(itemDir))
586   {
587     CMediaSource itemSource;
588     itemSource.strName = g_localizeStrings.Get(36041);
589     itemSource.strPath = itemDir;
590     sources.push_back(itemSource);
591   }
592 }