diff options
author | Dave Blake <oak99sky@yahoo.co.uk> | 2018-03-01 17:17:18 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-01 17:17:18 +0000 |
commit | 395fd57bd029044325cd7720c3a177a85f7e28c9 (patch) | |
tree | d50345b7adaffa5dd6c9c9bed6c8457b8abba9b3 | |
parent | 5d9b965ffa0620d8cd6012b1f7ee036e02c6c6e5 (diff) | |
parent | 70f651062e5b74b33ad99b489b9403428de8f244 (diff) |
Merge pull request #13533 from DaveTBlake/JobInfoDialog
[Music]Refactor Song Information Dialog
-rw-r--r-- | addons/resource.language.en_gb/resources/strings.po | 34 | ||||
-rw-r--r-- | xbmc/Application.cpp | 134 | ||||
-rw-r--r-- | xbmc/FileItem.cpp | 4 | ||||
-rw-r--r-- | xbmc/GUIInfoManager.cpp | 12 | ||||
-rw-r--r-- | xbmc/music/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xbmc/music/MusicDatabase.cpp | 87 | ||||
-rw-r--r-- | xbmc/music/MusicDatabase.h | 9 | ||||
-rw-r--r-- | xbmc/music/MusicInfoLoader.cpp | 34 | ||||
-rw-r--r-- | xbmc/music/MusicUtils.cpp | 240 | ||||
-rw-r--r-- | xbmc/music/MusicUtils.h | 69 | ||||
-rw-r--r-- | xbmc/music/dialogs/GUIDialogMusicOSD.cpp | 52 | ||||
-rw-r--r-- | xbmc/music/dialogs/GUIDialogSongInfo.cpp | 428 | ||||
-rw-r--r-- | xbmc/music/dialogs/GUIDialogSongInfo.h | 25 | ||||
-rw-r--r-- | xbmc/music/windows/GUIWindowMusicBase.cpp | 13 |
14 files changed, 820 insertions, 323 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index b138c67038..d73e319fc0 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -2589,6 +2589,7 @@ msgstr "" #: xbmc/dialogs/GUIDialogMediaFilter.cpp #: xbmc/playlists/SmartPlaylist.cpp +#: xbmc/music/MusicUtils.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp #: addons/skin.estuary/xml/DialogVideoInfo.xml #: addons/skin.estuary/xml/DialogFullScreenInfo.xml @@ -3955,7 +3956,6 @@ msgstr "" #: xbmc/dialogs/GUIDialogFavourites.cpp #: xbmc/music/dialogs/GUIDialogMusicInfo.cpp -#: xbmc/music/dialogs/GUIDialogSongInfo.cpp msgctxt "#1030" msgid "Browse for image" msgstr "" @@ -7171,11 +7171,13 @@ msgctxt "#13510" msgid "Sync playback to display" msgstr "" +#: xbmc/music/dialogs/GUIDialogSongInfo.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp msgctxt "#13511" msgid "Choose art" msgstr "" +#: xbmc/music/dialogs/GUIDialogSongInfo.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp msgctxt "#13512" msgid "Current art" @@ -7191,14 +7193,17 @@ msgctxt "#13514" msgid "Local art" msgstr "" +#: xbmc/music/dialogs/GUIDialogSongInfo.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp msgctxt "#13515" msgid "No art" msgstr "" +#. Button to add a new type of art e.g. "thumb", "poster", "fanart" etc. +#: xbmc/music/MusicUtils.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp msgctxt "#13516" -msgid "Add art" +msgid "Add art type" msgstr "" #: system/settings/settings.xml @@ -7221,7 +7226,13 @@ msgctxt "#13520" msgid "Embedded fanart" msgstr "" -#empty strings from id 13521 to 13549 +#. Heading for selection of a type of art e.g. "thumb", "poster", "fanart" etc. +#: xbmc/music/MusicUtils.cpp +msgctxt "#13521" +msgid "Choose art type" +msgstr "" + +#empty strings from id 13522 to 13549 #: system/settings/settings.xml msgctxt "#13550" @@ -11804,7 +11815,6 @@ msgstr "" #: xbmc/dialogs/GUIDialogContextMenu.cpp #: xbmc/music/dialogs/GUIDialogMusicInfo.cpp -#: xbmc/music/dialogs/GUIDialogSongInfo.cpp msgctxt "#20016" msgid "Current thumb" msgstr "" @@ -11818,7 +11828,6 @@ msgstr "" #: xbmc/dialogs/GUIDialogContextMenu.cpp #: xbmc/music/dialogs/GUIDialogMusicInfo.cpp -#: xbmc/music/dialogs/GUIDialogSongInfo.cpp msgctxt "#20018" msgid "No thumb" msgstr "" @@ -11830,6 +11839,7 @@ msgid "Choose thumbnail" msgstr "" #. Name of an artwork type +#: xbmc/music/MusicUtils.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp #: addons/skin.estuary/xml/View_501_Banner.xml msgctxt "#20020" @@ -11837,6 +11847,7 @@ msgid "Banner" msgstr "" #. Name of an artwork type +#: xbmc/music/MusicUtils.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp #: addons/skin.estuary/xml/View_51_Poster.xml msgctxt "#20021" @@ -13397,6 +13408,8 @@ msgctxt "#20444" msgid "Add to library" msgstr "" +#. Name of an artwork type +#: xbmc/music/MusicUtils.cpp #: xbmc/music/dialogs/GUIDialogMusicInfo.cpp #: addons/skin.estuary/xml/View_502_FanArt.xml msgctxt "#20445" @@ -13669,6 +13682,11 @@ msgctxt "#21370" msgid "Play GUI sounds during media playback" msgstr "" +#. Name of an artwork type +#: xbmc/music/MusicUtils.cpp +#: xbmc/music/dialogs/GUIDialogSongInfo.cpp +#: xbmc/video/dialogs/GUIDialogVideoInfo.cpp +#: xbmc/dialogs/GUIDialogFileBrowser.h msgctxt "#21371" msgid "Thumbnail" msgstr "" @@ -20857,12 +20875,16 @@ msgstr "" #empty strings from id 38019 to 38021 -#. Used for the viewstate selection +#. Used for the rating selection +#: xbmc/music/MusicUtils.cpp #: xbmc/video/dialogs/GUIDialogVideoInfo.cpp msgctxt "#38022" msgid "No rating" msgstr "" +#. Used for the rating selection +#: xbmc/music/MusicUtils.cpp +#: xbmc/video/dialogs/GUIDialogVideoInfo.cpp msgctxt "#38023" msgid "Set my rating" msgstr "" diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index a2c1eee631..8fe1ed5da5 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -126,6 +126,7 @@ #include "peripherals/Peripherals.h" #include "peripherals/devices/PeripheralImon.h" #include "music/infoscanner/MusicInfoScanner.h" +#include "music/MusicUtils.h" // Windows includes #include "guilib/GUIWindowManager.h" @@ -1977,69 +1978,87 @@ bool CApplication::OnAction(const CAction &action) return true; } - if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_appPlayer.IsPlayingAudio()) + if ((action.GetID() == ACTION_SET_RATING) && m_appPlayer.IsPlayingAudio()) { - const CMusicInfoTag *tag = g_infoManager.GetCurrentSongTag(); - if (tag) + int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_itemCurrentFile->GetMusicInfoTag()->GetUserrating()); + if (userrating < 0) // Nothing selected, so user rating unchanged + return true; + userrating = std::min(userrating, 10); + if (userrating != m_itemCurrentFile->GetMusicInfoTag()->GetUserrating()) { - *m_itemCurrentFile->GetMusicInfoTag() = *tag; - int userrating = tag->GetUserrating(); - bool needsUpdate(false); - if (userrating > 0 && action.GetID() == ACTION_DECREASE_RATING) - { - m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating - 1); - needsUpdate = true; - } - else if (userrating < 10 && action.GetID() == ACTION_INCREASE_RATING) - { - m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating + 1); - needsUpdate = true; - } - if (needsUpdate) - { - CMusicDatabase db; - if (db.Open()) // OpenForWrite() ? - { - db.SetSongUserrating(m_itemCurrentFile->GetPath(), m_itemCurrentFile->GetMusicInfoTag()->GetUserrating()); - db.Close(); - } - // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile); - g_windowManager.SendMessage(msg); - } + m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating); + // Mirror changes to GUI item + g_infoManager.SetCurrentItem(*m_itemCurrentFile); + + // Asynchronously update song userrating in music library + MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, userrating); + + // Tell all windows (e.g. playlistplayer, media windows) to update the fileitem + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile); + g_windowManager.SendMessage(msg); + } + return true; + } + + else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_appPlayer.IsPlayingAudio()) + { + int userrating = m_itemCurrentFile->GetMusicInfoTag()->GetUserrating(); + bool needsUpdate(false); + if (userrating > 0 && action.GetID() == ACTION_DECREASE_RATING) + { + m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating - 1); + needsUpdate = true; + } + else if (userrating < 10 && action.GetID() == ACTION_INCREASE_RATING) + { + m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating + 1); + needsUpdate = true; + } + if (needsUpdate) + { + // Mirror changes to current GUI item + g_infoManager.SetCurrentItem(*m_itemCurrentFile); + + // Asynchronously update song userrating in music library + MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, m_itemCurrentFile->GetMusicInfoTag()->GetUserrating()); + + // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile); + g_windowManager.SendMessage(msg); } + return true; } else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_appPlayer.IsPlayingVideo()) { - const CVideoInfoTag *tag = g_infoManager.GetCurrentMovieTag(); - if (tag) + int rating = m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating; + bool needsUpdate(false); + if (rating > 1 && action.GetID() == ACTION_DECREASE_RATING) { - *m_itemCurrentFile->GetVideoInfoTag() = *tag; - int rating = tag->m_iUserRating; - bool needsUpdate(false); - if (rating > 1 && action.GetID() == ACTION_DECREASE_RATING) - { - m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating - 1; - needsUpdate = true; - } - else if (rating < 10 && action.GetID() == ACTION_INCREASE_RATING) - { - m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating + 1; - needsUpdate = true; - } - if (needsUpdate) + m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating - 1; + needsUpdate = true; + } + else if (rating < 10 && action.GetID() == ACTION_INCREASE_RATING) + { + m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating + 1; + needsUpdate = true; + } + if (needsUpdate) + { + // Mirror changes to GUI item + g_infoManager.SetCurrentItem(*m_itemCurrentFile); + + CVideoDatabase db; + if (db.Open()) { - CVideoDatabase db; - if (db.Open()) - { - db.SetVideoUserRating(m_itemCurrentFile->GetVideoInfoTag()->m_iDbId, m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating, m_itemCurrentFile->GetVideoInfoTag()->m_type); - db.Close(); - } - // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile); - g_windowManager.SendMessage(msg); + db.SetVideoUserRating(m_itemCurrentFile->GetVideoInfoTag()->m_iDbId, + m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating, + m_itemCurrentFile->GetVideoInfoTag()->m_type); + db.Close(); } + // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile); + g_windowManager.SendMessage(msg); } return true; } @@ -3850,6 +3869,15 @@ bool CApplication::OnMessage(CGUIMessage& message) m_bInitializing = false; } + else if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem()) + { + CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem()); + if (m_itemCurrentFile->IsSamePath(item.get())) + { + m_itemCurrentFile->UpdateInfo(*item); + g_infoManager.SetCurrentItem(*m_itemCurrentFile); + } + } } break; diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index 925d5607f3..de93630a94 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -1606,8 +1606,8 @@ void CFileItem::UpdateInfo(const CFileItem &item, bool replaceLabels /*=true*/) SetLabel(item.GetLabel()); if (replaceLabels && !item.GetLabel2().empty()) SetLabel2(item.GetLabel2()); - if (!item.GetArt("thumb").empty()) - SetArt("thumb", item.GetArt("thumb")); + if (!item.GetArt().empty()) + SetArt(item.GetArt()); if (!item.GetIconImage().empty()) SetIconImage(item.GetIconImage()); AppendProperties(item); diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index f4a3524e73..1fa5b47edf 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -178,18 +178,6 @@ CGUIInfoManager::~CGUIInfoManager(void) bool CGUIInfoManager::OnMessage(CGUIMessage &message) { - if (message.GetMessage() == GUI_MSG_NOTIFY_ALL) - { - if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem()) - { - CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem()); - if (m_currentFile->IsSamePath(item.get())) - { - m_currentFile->UpdateInfo(*item); - return true; - } - } - } return false; } diff --git a/xbmc/music/CMakeLists.txt b/xbmc/music/CMakeLists.txt index 8b80e19669..5e1fd420fa 100644 --- a/xbmc/music/CMakeLists.txt +++ b/xbmc/music/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES Album.cpp MusicInfoLoader.cpp MusicLibraryQueue.cpp MusicThumbLoader.cpp + MusicUtils.cpp Song.cpp) set(HEADERS Album.h @@ -18,6 +19,7 @@ set(HEADERS Album.h MusicInfoLoader.h MusicLibraryQueue.h MusicThumbLoader.h + MusicUtils.h Song.h) core_add_library(music) diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp index ed8cf2b487..e9c081fb78 100644 --- a/xbmc/music/MusicDatabase.cpp +++ b/xbmc/music/MusicDatabase.cpp @@ -5723,6 +5723,39 @@ int CMusicDatabase::GetArtistByMatch(const CArtist& artist) return -1; } +bool CMusicDatabase::GetArtistFromSong(int idSong, CArtist &artist) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL = PrepareSQL( + "SELECT artistview.* FROM song_artist " + "JOIN artistview ON song_artist.idArtist = artistview.idArtist " + "WHERE song_artist.idSong= %i AND song_artist.idRole = 1 AND song_artist.iOrder = 0", + idSong); + if (!m_pDS->query(strSQL)) return false; + int iRowsFound = m_pDS->num_rows(); + if (iRowsFound != 1) + { + m_pDS->close(); + return false; + } + + artist = GetArtistFromDataset(m_pDS.get()); + + m_pDS->close(); + return true; + + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + int CMusicDatabase::GetAlbumByName(const std::string& strAlbum, const std::string& strArtist) { try @@ -6184,15 +6217,31 @@ bool CMusicDatabase::SetSongUserrating(const std::string &filePath, int userrati if (NULL == m_pDS.get()) return false; int songID = GetSongIDFromPath(filePath); - if (-1 == songID) return false; + if (-1 == songID) return false; + + return SetSongUserrating(songID, userrating); + } + catch (...) + { + CLog::Log(LOGERROR, "%s (%s,%i) failed", __FUNCTION__, filePath.c_str(), userrating); + } + return false; +} - std::string sql = PrepareSQL("UPDATE song SET userrating='%i' WHERE idSong = %i", userrating, songID); +bool CMusicDatabase::SetSongUserrating(int idSong, int userrating) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("UPDATE song SET userrating='%i' WHERE idSong = %i", userrating, idSong); m_pDS->exec(sql); return true; } catch (...) { - CLog::Log(LOGERROR, "%s (%s,%i) failed", __FUNCTION__, filePath.c_str(), userrating); + CLog::Log(LOGERROR, "%s (%i,%i) failed", __FUNCTION__, idSong, userrating); } return false; } @@ -7229,6 +7278,38 @@ bool CMusicDatabase::RemoveArtForItem(int mediaId, const MediaType & mediaType, return result; } +bool CMusicDatabase::GetArtTypes(const MediaType &mediaType, std::vector<std::string> &artTypes) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str()); + + if (!m_pDS->query(strSQL)) return false; + int iRowsFound = m_pDS->num_rows(); + if (iRowsFound == 0) + { + m_pDS->close(); + return false; + } + + while (!m_pDS->eof()) + { + artTypes.emplace_back(m_pDS->fv(0).get_asString()); + m_pDS->next(); + } + m_pDS->close(); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str()); + } + return false; +} + bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription &sorting) { if (!musicUrl.IsValid()) diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h index 66f967c1a9..deea2a1c56 100644 --- a/xbmc/music/MusicDatabase.h +++ b/xbmc/music/MusicDatabase.h @@ -208,6 +208,7 @@ public: bool Search(const std::string& search, CFileItemList &items); bool RemoveSongsFromPath(const std::string &path, MAPSONGS& songs, bool exact=true); bool SetSongUserrating(const std::string &filePath, int userrating); + bool SetSongUserrating(int idSong, int userrating); bool SetSongVotes(const std::string &filePath, int votes); int GetSongByArtistAndAlbumAndTitle(const std::string& strArtist, const std::string& strAlbum, const std::string& strTitle); @@ -321,6 +322,7 @@ public: std::string GetArtistById(int id); int GetArtistByName(const std::string& strArtist); int GetArtistByMatch(const CArtist& artist); + bool GetArtistFromSong(int idSong, CArtist &artist); std::string GetRoleById(int id); /*! \brief Propagate artist sort name into the concatenated artist sort name strings @@ -552,6 +554,13 @@ public: */ bool RemoveArtForItem(int mediaId, const MediaType &mediaType, const std::set<std::string> &artTypes); + /*! \brief Fetch the distinct types of art held in the database for a type of media. + \param mediaType the type of media, which corresponds to the table the item resides in (song/artist/album). + \param artTypes [out] the types of art e.g. "thumb", "fanart", etc. + \return true if art is found, false if no art is found. + */ + bool GetArtTypes(const MediaType &mediaType, std::vector<std::string> &artTypes); + ///////////////////////////////////////////////// // Tag Scan Version ///////////////////////////////////////////////// diff --git a/xbmc/music/MusicInfoLoader.cpp b/xbmc/music/MusicInfoLoader.cpp index ca1d9b49f2..d8bd95a980 100644 --- a/xbmc/music/MusicInfoLoader.cpp +++ b/xbmc/music/MusicInfoLoader.cpp @@ -91,20 +91,36 @@ bool CMusicInfoLoader::LoadAdditionalTagInfo(CFileItem* pItem) return false; // already have the information std::string path(pItem->GetPath()); - if (pItem->IsMusicDb()) + // For songs in library set the (primary) song artist and album properties + // Use song Id (not path) as called for items from either library or file view + if (pItem->GetMusicInfoTag()->GetDatabaseId() > 0) { - // set the artist / album properties - XFILE::MUSICDATABASEDIRECTORY::CQueryParams param; - XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem->GetPath(),param); - CArtist artist; CMusicDatabase database; database.Open(); - if (database.GetArtist(param.GetArtistId(), artist, false)) - CMusicDatabase::SetPropertiesFromArtist(*pItem,artist); + // May already have song artist ids as item property, otherwise fetch from song id + CArtist artist; + bool artistfound = false; + if (pItem->HasProperty("artistid")) + { + CVariant::const_iterator_array varid = pItem->GetProperty("artistid").begin_array(); + int idArtist = varid->asInteger(); + artistfound = database.GetArtist(idArtist, artist, false); + } + else + artistfound = database.GetArtistFromSong(pItem->GetMusicInfoTag()->GetDatabaseId(), artist); + if (artistfound) + CMusicDatabase::SetPropertiesFromArtist(*pItem, artist); + // May already have album id, otherwise fetch album from song id CAlbum album; - if (database.GetAlbum(param.GetAlbumId(), album, false)) - CMusicDatabase::SetPropertiesFromAlbum(*pItem,album); + bool albumfound = false; + int idAlbum = pItem->GetMusicInfoTag()->GetAlbumId(); + if (idAlbum > 0) + albumfound = database.GetAlbum(idAlbum, album, false); + else + albumfound = database.GetAlbumFromSong(pItem->GetMusicInfoTag()->GetDatabaseId(), album); + if (albumfound) + CMusicDatabase::SetPropertiesFromAlbum(*pItem, album); path = pItem->GetMusicInfoTag()->GetURL(); } diff --git a/xbmc/music/MusicUtils.cpp b/xbmc/music/MusicUtils.cpp new file mode 100644 index 0000000000..fc200aa24c --- /dev/null +++ b/xbmc/music/MusicUtils.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2018 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "MusicUtils.h" +#include "dialogs/GUIDialogSelect.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/Key.h" +#include "music/MusicDatabase.h" +#include "music/tags/MusicInfoTag.h" +#include "Util.h" +#include "utils/JobManager.h" + +using namespace MUSIC_INFO; +using namespace XFILE; + +namespace MUSIC_UTILS +{ + class CSetArtJob : public CJob + { + CFileItemPtr pItem; + std::string m_artType; + std::string m_newArt; + public: + CSetArtJob(const CFileItemPtr item, const std::string& type, const std::string& newArt) : + pItem(item), + m_artType(type), + m_newArt(newArt) + { } + + ~CSetArtJob(void) override = default; + + // Asynchronously update song, album or artist art in library + bool DoWork(void) override + { + CMusicInfoTag& tag = *pItem->GetMusicInfoTag(); + CMusicDatabase db; + if (db.Open()) + { + if (!m_newArt.empty()) + db.SetArtForItem(tag.GetDatabaseId(), tag.GetType(), m_artType, m_newArt); + else + db.RemoveArtForItem(tag.GetDatabaseId(), tag.GetType(), m_artType); + db.Close(); + } + return true; + } + }; + + class CSetSongRatingJob : public CJob + { + std::string strPath; + int idSong; + int iUserrating; + public: + CSetSongRatingJob(const std::string& filePath, int userrating) : + strPath(filePath), + idSong(-1), + iUserrating(userrating) + { } + + CSetSongRatingJob(int songId, int userrating) : + strPath(), + idSong(songId), + iUserrating(userrating) + { } + + ~CSetSongRatingJob(void) override = default; + + bool DoWork(void) override + { + // Asynchronously update song userrating in library + CMusicDatabase db; + if (db.Open()) + { + if (idSong > 0) + db.SetSongUserrating(idSong, iUserrating); + else + db.SetSongUserrating(strPath, iUserrating); + db.Close(); + } + + return true; + } + }; + + void UpdateArtJob(const CFileItemPtr pItem, const std::string& strType, const std::string& strArt) + { + // Asynchronously update that type of art in the database + CSetArtJob *job = new CSetArtJob(pItem, strType, strArt); + CJobManager::GetInstance().AddJob(job, NULL); + } + + bool FillArtTypesList(CFileItem& musicitem, CFileItemList& artlist) + { + CMusicInfoTag &tag = *musicitem.GetMusicInfoTag(); + if (tag.GetDatabaseId() < 1 || tag.GetType().empty()) + return false; + if (tag.GetType() != MediaTypeArtist && tag.GetType() != MediaTypeAlbum && tag.GetType() != MediaTypeSong) + return false; + + artlist.Clear(); + // Songs, albums and artists all have thumbs by default + std::vector<std::string> artTypes = { "thumb" }; + if (tag.GetType() == MediaTypeArtist) + { + artTypes.emplace_back("fanart"); + } + + CMusicDatabase db; + db.Open(); + + // Add in any stored art for this item that is non-empty. + std::map<std::string, std::string> currentArt; + db.GetArtForItem(tag.GetDatabaseId(), tag.GetType(), currentArt); + for (const auto art : currentArt) + { + if (!art.second.empty() && find(artTypes.begin(), artTypes.end(), art.first) == artTypes.end()) + artTypes.push_back(art.first); + } + + // Add any art types that exist for other media items of the same type + std::vector<std::string> dbArtTypes; + db.GetArtTypes(tag.GetType(), dbArtTypes); + for (const auto it : dbArtTypes) + { + if (find(artTypes.begin(), artTypes.end(), it) == artTypes.end()) + artTypes.push_back(it); + } + db.Close(); + + for (const auto type : artTypes) + { + CFileItemPtr artitem(new CFileItem(type, false)); + // Localise the names of common types of art + if (type == "banner") + artitem->SetLabel(g_localizeStrings.Get(20020)); + else if (type == "fanart") + artitem->SetLabel(g_localizeStrings.Get(20445)); + else if (type == "poster") + artitem->SetLabel(g_localizeStrings.Get(20021)); + else if (type == "thumb") + artitem->SetLabel(g_localizeStrings.Get(21371)); + else + artitem->SetLabel(type); + // Set art type as art item property + artitem->SetProperty("arttype", type); + // Set current art as art item thumb + if (musicitem.HasArt(type)) + artitem->SetArt("thumb", musicitem.GetArt(type)); + artlist.Add(artitem); + } + + return !artlist.IsEmpty(); + } + + std::string ShowSelectArtTypeDialog(CFileItemList& artitems) + { + // Prompt for choice + CGUIDialogSelect *dialog = g_windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (!dialog) + return ""; + + dialog->SetHeading(CVariant{ 13521 }); + dialog->Reset(); + dialog->SetUseDetails(true); + dialog->EnableButton(true, 13516); + + dialog->SetItems(artitems); + dialog->Open(); + + if (dialog->IsButtonPressed()) + { + // Get the new art type name + std::string strArtTypeName; + if (!CGUIKeyboardFactory::ShowAndGetInput(strArtTypeName, CVariant{ g_localizeStrings.Get(13516) }, false)) + return ""; + // Add new type to the list of art types + CFileItemPtr artitem(new CFileItem(strArtTypeName, false)); + artitem->SetLabel(strArtTypeName); + artitem->SetProperty("arttype", strArtTypeName); + artitems.Add(artitem); + + return strArtTypeName; + } + + return dialog->GetSelectedFileItem()->GetProperty("arttype").asString(); + } + + int ShowSelectRatingDialog(int iSelected) + { + CGUIDialogSelect *dialog = g_windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (dialog) + { + dialog->SetHeading(CVariant{ 38023 }); + dialog->Add(g_localizeStrings.Get(38022)); + for (int i = 1; i <= 10; i++) + dialog->Add(StringUtils::Format("%s: %i", g_localizeStrings.Get(563).c_str(), i)); + dialog->SetSelected(iSelected); + dialog->Open(); + + int userrating = dialog->GetSelectedItem(); + userrating = std::max(userrating, -1); + userrating = std::min(userrating, 10); + return userrating; + } + return -1; + } + + void UpdateSongRatingJob(const CFileItemPtr pItem, int userrating) + { + // Asynchronously update the song user rating in music library + const CMusicInfoTag *tag = pItem->GetMusicInfoTag(); + CSetSongRatingJob *job; + if (tag && tag->GetType() == MediaTypeSong && tag->GetDatabaseId() > 0) + // Use song ID when known + job = new CSetSongRatingJob(tag->GetDatabaseId(), userrating); + else + job = new CSetSongRatingJob(pItem->GetPath(), userrating); + CJobManager::GetInstance().AddJob(job, NULL); + } +} diff --git a/xbmc/music/MusicUtils.h b/xbmc/music/MusicUtils.h new file mode 100644 index 0000000000..44a1467b86 --- /dev/null +++ b/xbmc/music/MusicUtils.h @@ -0,0 +1,69 @@ +#pragma once + +/* + * Copyright (C) 2018 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "FileItem.h" + +namespace MUSIC_UTILS +{ + /*! \brief Show a dialog to allow the selection of type of art from a list. + Input is a fileitem list, with each item having an "arttype" property + e.g. "thumb", current art URL (if art exists), and label. One of these art types + can be selected, or a new art type added. The new art type is added as a new item + in the list, as well as retuned as the selected art type. + \param artitems [in/out] a fileitem list to display + \return the selected art type e.g. "fanart" or empty string when cancelled. + \sa FillArtTypesList + */ + std::string ShowSelectArtTypeDialog(CFileItemList& artitems); + + /*! \brief Helper function to build a list of art types for a music library item. + This fetches the possible types of art for a song, album or artist, and the + current art URL (if the item has art of that type), for display on a dialog. + \param musicitem a music CFileItem (song, album or artist) + \param artitems [out] a fileitem list, each item having "arttype" property + e.g. "thumb", current art URL (if art exists), and localized label (for common arttypes) + \return true if art types are retrieved, false if none is found. + \sa ShowSelectArtTypeDialog + */ + bool FillArtTypesList(CFileItem& musicitem, CFileItemList& artlist); + + /*! \brief Helper function to asynchronously update art in the music database + For the song, album or artist this adds a job to the queue to update the art table + modifying, adding or deleting that type of art, + \param item a shared pointer to a music CFileItem (song, album or artist) + \param strType the type of art e.g. "fanart" or "thumb" etc. + \param strArt art URL, when empty the entry for that type of art is deleted. + */ + void UpdateArtJob(const CFileItemPtr pItem, const std::string& strType, const std::string& strArt); + + /*! \brief Show a dialog to allow the selection of user rating. + \param iSelected the rating to show initially + \return the selected rating, 0 (no rating), 1 to 10 or -1 no rating selected + */ + int ShowSelectRatingDialog(int iSelected); + +/*! \brief Helper function to asynchronously update the user rating of a song +\param pItem pointer to song item being rated +\param userrating the userrating 0 = no rating, 1 to 10 +*/ + void UpdateSongRatingJob(const CFileItemPtr pItem, int userrating); +} diff --git a/xbmc/music/dialogs/GUIDialogMusicOSD.cpp b/xbmc/music/dialogs/GUIDialogMusicOSD.cpp index 0ab3a9fc1a..a7d4ef28de 100644 --- a/xbmc/music/dialogs/GUIDialogMusicOSD.cpp +++ b/xbmc/music/dialogs/GUIDialogMusicOSD.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2013 Team XBMC + * Copyright (C) 2005-2018 Team Kodi * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify @@ -19,19 +19,12 @@ */ #include "GUIDialogMusicOSD.h" -#include "ServiceBroker.h" +#include "addons/GUIWindowAddonBrowser.h" #include "guilib/GUIWindowManager.h" -#include "guilib/LocalizeStrings.h" +#include "GUIUserMessages.h" #include "input/Key.h" #include "input/InputManager.h" -#include "GUIUserMessages.h" #include "settings/Settings.h" -#include "addons/GUIWindowAddonBrowser.h" -#include "xbmc/dialogs/GUIDialogSelect.h" -#include "xbmc/Application.h" -#include "xbmc/FileItem.h" -#include "xbmc/music/tags/MusicInfoTag.h" -#include "xbmc/music/MusicDatabase.h" #include "ServiceBroker.h" #define CONTROL_VIS_BUTTON 500 @@ -81,45 +74,6 @@ bool CGUIDialogMusicOSD::OnAction(const CAction &action) case ACTION_SHOW_OSD: Close(); return true; - - case ACTION_SET_RATING: - { - CGUIDialogSelect *dialog = g_windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); - if (dialog) - { - dialog->SetHeading(CVariant{ 38023 }); - dialog->Add(g_localizeStrings.Get(38022)); - for (int i = 1; i <= 10; i++) - dialog->Add(StringUtils::Format("%s: %i", g_localizeStrings.Get(563).c_str(), i)); - - auto track = g_application.CurrentFileItemPtr(); - dialog->SetSelected(track->GetMusicInfoTag()->GetUserrating()); - - dialog->Open(); - - int userrating = dialog->GetSelectedItem(); - - if (userrating < 0) userrating = 0; - if (userrating > 10) userrating = 10; - if (userrating != track->GetMusicInfoTag()->GetUserrating()) - { - track->GetMusicInfoTag()->SetUserrating(userrating); - // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, track); - g_windowManager.SendMessage(msg); - - CMusicDatabase db; - if (db.Open()) - { - db.SetSongUserrating(track->GetMusicInfoTag()->GetURL(), userrating); - db.Close(); - } - } - - } - return true; - } - default: break; } diff --git a/xbmc/music/dialogs/GUIDialogSongInfo.cpp b/xbmc/music/dialogs/GUIDialogSongInfo.cpp index 4cc89cb065..be8025193b 100644 --- a/xbmc/music/dialogs/GUIDialogSongInfo.cpp +++ b/xbmc/music/dialogs/GUIDialogSongInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2013 Team XBMC + * Copyright (C) 2005-2018 Team XBMC * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify @@ -19,26 +19,23 @@ */ #include "GUIDialogSongInfo.h" -#include "utils/URIUtils.h" -#include "utils/StringUtils.h" -#include "utils/Variant.h" +#include "dialogs/GUIDialogBusy.h" #include "dialogs/GUIDialogFileBrowser.h" #include "dialogs/GUIDialogSelect.h" -#include "GUIPassword.h" +#include "filesystem/File.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "GUIDialogMusicInfo.h" #include "GUIUserMessages.h" +#include "input/Key.h" +#include "music/Album.h" #include "music/MusicDatabase.h" -#include "music/windows/GUIWindowMusicBase.h" +#include "music/MusicUtils.h" #include "music/tags/MusicInfoTag.h" -#include "guilib/GUIWindowManager.h" -#include "input/Key.h" -#include "filesystem/File.h" -#include "filesystem/CurlFile.h" -#include "FileItem.h" +#include "music/windows/GUIWindowMusicBase.h" #include "settings/MediaSourceSettings.h" -#include "guilib/LocalizeStrings.h" -#include "music/Album.h" #include "storage/MediaManager.h" -#include "GUIDialogMusicInfo.h" +#include "Util.h" using namespace XFILE; @@ -49,14 +46,82 @@ using namespace XFILE; #define CONTROL_LIST 50 +#define TIME_TO_BUSY_DIALOG 500 + + + +class CGetSongInfoJob : public CJob +{ +public: + ~CGetSongInfoJob(void) override = default; + + // Fetch full song information including art types list + bool DoWork() override + { + CGUIDialogSongInfo *dialog = g_windowManager.GetWindow<CGUIDialogSongInfo>(WINDOW_DIALOG_SONG_INFO); + if (!dialog) + return false; + if (dialog->IsCancelled()) + return false; + CFileItemPtr m_song = dialog->GetCurrentListItem(); + + // Fetch tag data from library using filename, or scanning file (if item not already have this loaded) + m_song->LoadMusicTag(); + if (dialog->IsCancelled()) + return false; + // Fetch album and primary song artist data from library as properties + // and lyrics by scanning tags from file + MUSIC_INFO::CMusicInfoLoader::LoadAdditionalTagInfo(m_song.get()); + if (dialog->IsCancelled()) + return false; + + // Load song art. + // For songs in library this includes related album and artist(s) art, + // otherwise just embedded or cached thumb is fetched. + CMusicThumbLoader loader; + loader.LoadItem(m_song.get()); + if (dialog->IsCancelled()) + return false; + + // For songs in library fill vector of possible art types, with current art when it exists + // for display on the art type selection dialog + CFileItemList artlist; + MUSIC_UTILS::FillArtTypesList(*m_song, artlist); + dialog->SetArtTypeList(artlist); + if (dialog->IsCancelled()) + return false; + + // Fetch artist art for non db songs when artist can be found uniquely by name + if (!m_song->IsMusicDb() && m_song->HasMusicInfoTag() && !m_song->GetMusicInfoTag()->GetArtist().empty()) + { + CMusicDatabase db; + db.Open(); + int idArtist = db.GetArtistByName(m_song->GetMusicInfoTag()->GetArtist()[0]); + if (idArtist > 0) + { + std::map<std::string, std::string> art; + if (db.GetArtForItem(idArtist, MediaTypeArtist, art)) + m_song->AppendArt(art, "artist"); + } + db.Close(); + } + + // Tell waiting SongInfoDialog that job is complete + dialog->FetchComplete(); + + return true; + } +}; + CGUIDialogSongInfo::CGUIDialogSongInfo(void) : CGUIDialog(WINDOW_DIALOG_SONG_INFO, "DialogMusicInfo.xml") , m_song(new CFileItem) , m_albumId(-1) { m_cancelled = false; - m_needsUpdate = false; + m_hasUpdatedUserrating = false; m_startUserrating = -1; + m_artTypeList.Clear(); m_loadType = KEEP_IN_MEMORY; } @@ -68,15 +133,20 @@ bool CGUIDialogSongInfo::OnMessage(CGUIMessage& message) { case GUI_MSG_WINDOW_DEINIT: { + m_artTypeList.Clear(); if (m_startUserrating != m_song->GetMusicInfoTag()->GetUserrating()) { - CMusicDatabase db; - if (db.Open()) - { - m_needsUpdate = true; - db.SetSongUserrating(m_song->GetPath(), m_song->GetMusicInfoTag()->GetUserrating()); - db.Close(); - } + m_hasUpdatedUserrating = true; + + // Asynchronously update song userrating in library + MUSIC_UTILS::UpdateSongRatingJob(m_song, m_song->GetMusicInfoTag()->GetUserrating()); + + // Send a message to all windows to tell them to update the fileitem + // This communicates the rating change to the music lib window, current playlist and OSD. + // The music lib window item is updated to but changes to the rating when it is the sort + // do not show on screen until refresh() that fetchs the list from scratch, sorts etc. + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song); + g_windowManager.SendMessage(msg); } CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST); OnMessage(msg); @@ -110,7 +180,7 @@ bool CGUIDialogSongInfo::OnMessage(CGUIMessage& message) } else if (iControl == CONTROL_BTN_GET_THUMB) { - OnGetThumb(); + OnGetArt(); return true; } else if (iControl == CONTROL_LIST) @@ -146,7 +216,7 @@ bool CGUIDialogSongInfo::OnMessage(CGUIMessage& message) return CGUIDialog::OnMessage(message); } -bool CGUIDialogSongInfo::OnAction(const CAction &action) +bool CGUIDialogSongInfo::OnAction(const CAction& action) { int userrating = m_song->GetMusicInfoTag()->GetUserrating(); if (action.GetID() == ACTION_INCREASE_RATING) @@ -173,29 +243,18 @@ bool CGUIDialogSongInfo::OnBack(int actionID) return CGUIDialog::OnBack(actionID); } +void CGUIDialogSongInfo::FetchComplete() +{ + //Trigger the event to indicate data has been fetched + m_event.Set(); +} + void CGUIDialogSongInfo::OnInitWindow() { - // Normally have album id from song + // Enable album info button when we know album m_albumId = m_song->GetMusicInfoTag()->GetAlbumId(); - if (m_albumId < 0) - { - CMusicDatabase db; - db.Open(); - - // no known db info - check if parent dir is an album - if (m_song->GetMusicInfoTag()->GetDatabaseId() == -1) - { - std::string path = URIUtils::GetDirectory(m_song->GetPath()); - m_albumId = db.GetAlbumIdByPath(path); - } - else - { - CAlbum album; - db.GetAlbumFromSong(m_song->GetMusicInfoTag()->GetDatabaseId(), album); - m_albumId = album.idAlbum; - } - } - CONTROL_ENABLE_ON_CONDITION(CONTROL_ALBUMINFO, m_albumId > -1); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_ALBUMINFO, m_albumId > 0); // Disable music user rating button for plugins as they don't have tables to save this if (m_song->IsPlugin()) @@ -205,7 +264,7 @@ void CGUIDialogSongInfo::OnInitWindow() SET_CONTROL_HIDDEN(CONTROL_BTN_REFRESH); SET_CONTROL_LABEL(CONTROL_USERRATING, 38023); - SET_CONTROL_LABEL(CONTROL_BTN_GET_THUMB, 13405); + SET_CONTROL_LABEL(CONTROL_BTN_GET_THUMB, 13511); SET_CONTROL_LABEL(CONTROL_ALBUMINFO, 10523); CGUIDialog::OnInitWindow(); @@ -227,179 +286,198 @@ void CGUIDialogSongInfo::Update() void CGUIDialogSongInfo::SetUserrating(int userrating) { - if (userrating < 0) userrating = 0; - if (userrating > 10) userrating = 10; + userrating = std::max(userrating, 0); + userrating = std::min(userrating, 10); if (userrating != m_song->GetMusicInfoTag()->GetUserrating()) { m_song->GetMusicInfoTag()->SetUserrating(userrating); - // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song); - g_windowManager.SendMessage(msg); } } -void CGUIDialogSongInfo::SetSong(CFileItem *item) +bool CGUIDialogSongInfo::SetSong(CFileItem* item) { *m_song = *item; - m_song->LoadMusicTag(); - m_startUserrating = m_song->GetMusicInfoTag()->GetUserrating(); - MUSIC_INFO::CMusicInfoLoader::LoadAdditionalTagInfo(m_song.get()); - - // Load song art. - // For songs in library this includes related album and artist(s) art, - // otherwise just embedded or cached thumb is fetched. - CMusicThumbLoader loader; - loader.LoadItem(m_song.get()); - - // Set artist art for non db songs when artist can be found uniquely by name - if (!m_song->IsMusicDb() && m_song->HasMusicInfoTag() && !m_song->GetMusicInfoTag()->GetArtist().empty()) + m_event.Reset(); + m_cancelled = false; // SetSong happens before win_init + // In a separate job fetch song info and fill list of art types. + int jobid = CJobManager::GetInstance().AddJob(new CGetSongInfoJob(), nullptr, CJob::PRIORITY_LOW); + + // Wait to get all data before show, allowing user to to cancel if fetch is slow + if (!CGUIDialogBusy::WaitOnEvent(m_event, TIME_TO_BUSY_DIALOG)) { - CMusicDatabase db; - db.Open(); - int idArtist = db.GetArtistByName(m_song->GetMusicInfoTag()->GetArtist()[0]); - if (idArtist > 0) - { - std::map<std::string, std::string> art; - if (db.GetArtForItem(idArtist, MediaTypeArtist, art)) - m_song->SetArt(art); - } + // Cancel job still waiting in queue (unlikely) + CJobManager::GetInstance().CancelJob(jobid); + // Flag to stop job already in progress + m_cancelled = true; + return false; } - m_needsUpdate = false; + + // CurrentDirectory() returns m_artTypeList (a convenient CFileItemList) + // Set content so can return dialog CONTAINER_CONTENT as "songs" + m_artTypeList.SetContent("songs"); + // Copy art from ListItem so CONTAINER_ART returns song art + m_artTypeList.SetArt(m_song->GetArt()); + + // Store initial userrating + m_startUserrating = m_song->GetMusicInfoTag()->GetUserrating(); + m_hasUpdatedUserrating = false; + return true; } -CFileItemPtr CGUIDialogSongInfo::GetCurrentListItem(int offset) +void CGUIDialogSongInfo::SetArtTypeList(CFileItemList& artlist) { - return m_song; + m_artTypeList.Copy(artlist); } -bool CGUIDialogSongInfo::DownloadThumbnail(const std::string &thumbFile) +CFileItemPtr CGUIDialogSongInfo::GetCurrentListItem(int offset) { - //! @todo Obtain the source... - std::string source; - CCurlFile http; - http.Download(source, thumbFile); - return true; + return m_song; } -// Get Thumb from user choice. -// Options are: -// 1. Current thumb -// 2. AllMusic.com thumb -// 3. Local thumb -// 4. No thumb (if no Local thumb is available) - -//! @todo Currently no support for "embedded thumb" as there is no easy way to grab it -//! without sending a file that has this as it's album to this class -void CGUIDialogSongInfo::OnGetThumb() +/* + Allow user to choose artwork for the song + For each type of art the options are: + 1. Current art + 2. Local art (thumb found by filename) + 3. Embedded art (@todo) + 4. None + Note that songs are not scraped, hence there is no list of urls for possible remote art +*/ +void CGUIDialogSongInfo::OnGetArt() { - CFileItemList items; + std::string type = MUSIC_UTILS::ShowSelectArtTypeDialog(m_artTypeList); + if (type.empty()) + return; // Cancelled - - // Grab the thumbnail from the web - /* - std::string thumbFromWeb; - thumbFromWeb = URIUtils::AddFileToFolder(g_advancedSettings.m_cachePath, "allmusicThumb.jpg"); - if (DownloadThumbnail(thumbFromWeb)) + CFileItemList items; + CGUIListItem::ArtMap primeArt = m_song->GetArt(); // Song art without fallbacks + bool bHasArt = m_song->HasArt(type); + bool bFallback(false); + if (bHasArt) { - CFileItemPtr item(new CFileItem("thumb://allmusic.com", false)); - item->SetArt("thumb", thumbFromWeb); - item->SetLabel(g_localizeStrings.Get(20055)); - items.Add(item); - }*/ + // Check if that type of art is actually a fallback, e.g. album thumb or artist fanart + CGUIListItem::ArtMap::const_iterator i = primeArt.find(type); + bFallback = (i == primeArt.end()); + } - // Current thumb - if (CFile::Exists(m_song->GetArt("thumb"))) + // Build list of possible images of that art type + if (bHasArt) { + // Add item for current artwork, could a fallback from album/artist CFileItemPtr item(new CFileItem("thumb://Current", false)); - item->SetArt("thumb", m_song->GetArt("thumb")); - item->SetLabel(g_localizeStrings.Get(20016)); + item->SetArt("thumb", m_song->GetArt(type)); + item->SetIconImage("DefaultPicture.png"); + item->SetLabel(g_localizeStrings.Get(13512)); //! @todo: label fallback art so user knows? items.Add(item); } - - // local thumb - std::string cachedLocalThumb; - std::string localThumb(m_song->GetUserMusicThumb(true)); - if (m_song->IsMusicDb()) - { - CFileItem item(m_song->GetMusicInfoTag()->GetURL(), false); - localThumb = item.GetUserMusicThumb(true); + else if (m_song->HasArt("thumb")) + { // For missing art of that type add the thumb (when it exists and not a fallback) + CGUIListItem::ArtMap::const_iterator i = primeArt.find("thumb"); + if (i != primeArt.end()) + { + CFileItemPtr item(new CFileItem("thumb://Thumb", false)); + item->SetArt("thumb", m_song->GetArt("thumb")); + item->SetIconImage("DefaultAlbumCover.png"); + item->SetLabel(g_localizeStrings.Get(21371)); + items.Add(item); + } } - if (CFile::Exists(localThumb)) - { - CFileItemPtr item(new CFileItem("thumb://Local", false)); - item->SetArt("thumb", localThumb); - item->SetLabel(g_localizeStrings.Get(20017)); - items.Add(item); + + std::string localThumb; + if (type == "thumb") + { // Local thumb type art held in <filename>.tbn (for non-library items) + localThumb = m_song->GetUserMusicThumb(true); + if (m_song->IsMusicDb()) + { + CFileItem item(m_song->GetMusicInfoTag()->GetURL(), false); + localThumb = item.GetUserMusicThumb(true); + } + if (CFile::Exists(localThumb)) + { + CFileItemPtr item(new CFileItem("thumb://Local", false)); + item->SetArt("thumb", localThumb); + item->SetLabel(g_localizeStrings.Get(20017)); + items.Add(item); + } } - else - { // no local thumb exists, so we are just using the allmusic.com thumb or cached thumb - // which is probably the allmusic.com thumb. These could be wrong, so allow the user - // to delete the incorrect thumb + + if (bHasArt && !bFallback) + { // Actually has this type of art (not a fallback) so + // allow the user to delete it by selecting "no art". CFileItemPtr item(new CFileItem("thumb://None", false)); item->SetArt("thumb", "DefaultAlbumCover.png"); - item->SetLabel(g_localizeStrings.Get(20018)); + item->SetLabel(g_localizeStrings.Get(13515)); items.Add(item); } + //! @todo: Add support for extracting embedded art + + // Show list of possible art for user selection std::string result; VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("music")); CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, *m_song); g_mediaManager.GetLocalDrives(sources); - if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(1030), result)) - return; // user cancelled - - if (result == "thumb://Current") - return; // user chose the one they have - - // delete the thumbnail if that's what the user wants, else overwrite with the - // new thumbnail - - std::string newThumb; - if (result == "thumb://None") - newThumb = ""; - else if (result == "thumb://allmusic.com") - newThumb.clear(); - else if (result == "thumb://Local") - newThumb = localThumb; - else - newThumb = result; - - // update thumb in the database - CMusicDatabase db; - if (db.Open()) + if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) && + result != "thumb://Current") { - db.SetArtForItem(m_song->GetMusicInfoTag()->GetDatabaseId(), m_song->GetMusicInfoTag()->GetType(), "thumb", newThumb); - db.Close(); - } + // User didn't choose the one they have, or the fallback image. + // Overwrite with the new art or clear it + std::string newArt; + if (result == "thumb://Thumb") + newArt = m_song->GetArt("thumb"); + else if (result == "thumb://Local") + newArt = localThumb; +// else if (result == "thumb://Embedded") +// newArt = embeddedArt; + else if (CFile::Exists(result)) + newArt = result; + else // none + newArt.clear(); + + // Asynchronously update that type of art in the database + MUSIC_UTILS::UpdateArtJob(m_song, type, newArt); + + // Update local song with current art + if (newArt.empty()) + { + // Remove that type of art from the song + primeArt.erase(type); + m_song->SetArt(primeArt); + } + else + // Add or modify the type of art + m_song->SetArt(type, newArt); + + // Update local art list with current art + // Show any fallback art when song art removed + if (newArt.empty() && m_song->HasArt(type)) + newArt = m_song->GetArt(type); + for (const auto artitem : m_artTypeList) + { + if (artitem->GetProperty("artType") == type) + { + artitem->SetArt("thumb", newArt); + break; + } + } - m_song->SetArt("thumb", newThumb); + // Get new artwork to show in other places e.g. on music lib window, + // current playlist and player OSD. + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song); + g_windowManager.SendMessage(msg); - // tell our GUI to completely reload all controls (as some of them - // are likely to have had this image in use so will need refreshing) - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS); - g_windowManager.SendMessage(msg); + } -// m_hasUpdatedThumb = true; + // Re-open the art type selection dialog as we come back from + // the image selection dialog + OnGetArt(); } void CGUIDialogSongInfo::OnSetUserrating() { - CGUIDialogSelect *dialog = g_windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); - if (dialog) - { - dialog->SetHeading(CVariant{ 38023 }); - dialog->Add(g_localizeStrings.Get(38022)); - for (int i = 1; i <= 10; i++) - dialog->Add(StringUtils::Format("%s: %i", g_localizeStrings.Get(563).c_str(), i)); - - dialog->SetSelected(m_song->GetMusicInfoTag()->GetUserrating()); - - dialog->Open(); + int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_song->GetMusicInfoTag()->GetUserrating()); + if (userrating < 0) // Nothing selected, so rating unchanged + return; - int iItem = dialog->GetSelectedItem(); - if (iItem < 0) - return; - - SetUserrating(iItem); - } -} + SetUserrating(userrating); +}
\ No newline at end of file diff --git a/xbmc/music/dialogs/GUIDialogSongInfo.h b/xbmc/music/dialogs/GUIDialogSongInfo.h index 6237070a27..61928f4dfd 100644 --- a/xbmc/music/dialogs/GUIDialogSongInfo.h +++ b/xbmc/music/dialogs/GUIDialogSongInfo.h @@ -1,7 +1,7 @@ #pragma once /* - * Copyright (C) 2005-2013 Team XBMC + * Copyright (C) 2005-2018 Team XBMC * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify @@ -21,8 +21,8 @@ */ #include "guilib/GUIDialog.h" - -class CFileItem; +#include "FileItem.h" +#include "threads/Event.h" class CGUIDialogSongInfo : public CGUIDialog @@ -31,24 +31,31 @@ public: CGUIDialogSongInfo(void); ~CGUIDialogSongInfo(void) override; bool OnMessage(CGUIMessage& message) override; - void SetSong(CFileItem *item); - bool OnAction(const CAction &action) override; + bool SetSong(CFileItem* item); + void SetArtTypeList(CFileItemList& artlist); + bool OnAction(const CAction& action) override; bool OnBack(int actionID) override; - bool NeedsUpdate() const { return m_needsUpdate; }; + bool HasUpdatedUserrating() const { return m_hasUpdatedUserrating; }; bool HasListItems() const override { return true; }; CFileItemPtr GetCurrentListItem(int offset = 0) override; + const CFileItemList& CurrentDirectory() const { return m_artTypeList; }; + bool IsCancelled() const { return m_cancelled; }; + void FetchComplete(); + protected: void OnInitWindow() override; void Update(); - bool DownloadThumbnail(const std::string &thumbFile); - void OnGetThumb(); + void OnGetArt(); void SetUserrating(int userrating); void OnSetUserrating(); CFileItemPtr m_song; + CFileItemList m_artTypeList; + CEvent m_event; int m_startUserrating; bool m_cancelled; - bool m_needsUpdate; + bool m_hasUpdatedUserrating; long m_albumId; + }; diff --git a/xbmc/music/windows/GUIWindowMusicBase.cpp b/xbmc/music/windows/GUIWindowMusicBase.cpp index 876ac3cc18..4470a087cf 100644 --- a/xbmc/music/windows/GUIWindowMusicBase.cpp +++ b/xbmc/music/windows/GUIWindowMusicBase.cpp @@ -173,7 +173,7 @@ bool CGUIWindowMusicBase::OnMessage(CGUIMessage& message) // update the display case GUI_MSG_SCAN_FINISHED: - case GUI_MSG_REFRESH_THUMBS: + case GUI_MSG_REFRESH_THUMBS: // Never called as is secondary msg sent as GUI_MSG_NOTIFY_ALL Refresh(); break; @@ -528,10 +528,13 @@ void CGUIWindowMusicBase::ShowSongInfo(CFileItem* pItem) if (!pItem->HasMusicInfoTag()) return; - dialog->SetSong(pItem); - dialog->Open(); - if (dialog->NeedsUpdate()) - Refresh(true); // update our file list + if (dialog->SetSong(pItem)) // Fetch full song info asynchronously + { + dialog->Open(); + if (dialog->HasUpdatedUserrating() && m_vecItems->GetSortMethod() == SortByUserRating) + // Refresh list to sort items and show new userrating sort label + Refresh(); + } } } |