/* * Copyright (C) 2012-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "guilib/guiinfo/MusicGUIInfo.h" #include "FileItem.h" #include "PartyModeManager.h" #include "PlayListPlayer.h" #include "ServiceBroker.h" #include "URL.h" #include "Util.h" #include "application/Application.h" #include "application/ApplicationComponents.h" #include "application/ApplicationPlayer.h" #include "guilib/LocalizeStrings.h" #include "guilib/guiinfo/GUIInfo.h" #include "guilib/guiinfo/GUIInfoHelper.h" #include "guilib/guiinfo/GUIInfoLabels.h" #include "music/MusicInfoLoader.h" #include "music/MusicThumbLoader.h" #include "music/tags/MusicInfoTag.h" #include "playlists/PlayList.h" #include "settings/AdvancedSettings.h" #include "settings/SettingsComponent.h" #include "utils/URIUtils.h" #include "utils/log.h" using namespace KODI::GUILIB; using namespace KODI::GUILIB::GUIINFO; using namespace MUSIC_INFO; bool CMusicGUIInfo::InitCurrentItem(CFileItem *item) { const auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); if (item && (item->IsAudio() || (item->IsInternetStream() && appPlayer->IsPlayingAudio()))) { CLog::Log(LOGDEBUG, "CMusicGUIInfo::InitCurrentItem({})", item->GetPath()); item->LoadMusicTag(); CMusicInfoTag* tag = item->GetMusicInfoTag(); // creates item if not yet set, so no nullptr checks needed tag->SetLoaded(true); // find a thumb for this file. if (item->IsInternetStream() && !item->IsMusicDb()) { if (!g_application.m_strPlayListFile.empty()) { CLog::Log(LOGDEBUG, "Streaming media detected... using {} to find a thumb", g_application.m_strPlayListFile); CFileItem streamingItem(g_application.m_strPlayListFile,false); CMusicThumbLoader loader; loader.FillThumb(streamingItem); if (streamingItem.HasArt("thumb")) item->SetArt("thumb", streamingItem.GetArt("thumb")); } } else { CMusicThumbLoader loader; loader.LoadItem(item); } CMusicInfoLoader::LoadAdditionalTagInfo(item); return true; } return false; } bool CMusicGUIInfo::GetLabel(std::string& value, const CFileItem *item, int contextWindow, const CGUIInfo &info, std::string *fallback) const { // For musicplayer "offset" and "position" info labels check playlist if (info.GetData1() && ((info.m_info >= MUSICPLAYER_OFFSET_POSITION_FIRST && info.m_info <= MUSICPLAYER_OFFSET_POSITION_LAST) || (info.m_info >= PLAYER_OFFSET_POSITION_FIRST && info.m_info <= PLAYER_OFFSET_POSITION_LAST))) return GetPlaylistInfo(value, info); const CMusicInfoTag* tag = item->GetMusicInfoTag(); if (tag) { switch (info.m_info) { ///////////////////////////////////////////////////////////////////////////////////////////// // PLAYER_* / MUSICPLAYER_* / LISTITEM_* ///////////////////////////////////////////////////////////////////////////////////////////// case PLAYER_PATH: case PLAYER_FILENAME: case PLAYER_FILEPATH: value = tag->GetURL(); if (value.empty()) value = item->GetPath(); value = GUIINFO::GetFileInfoLabelValueFromPath(info.m_info, value); return true; case PLAYER_TITLE: value = tag->GetTitle(); return !value.empty(); case MUSICPLAYER_TITLE: value = tag->GetTitle(); return !value.empty(); case LISTITEM_TITLE: value = tag->GetTitle(); return true; case MUSICPLAYER_PLAYCOUNT: case LISTITEM_PLAYCOUNT: if (tag->GetPlayCount() > 0) { value = std::to_string(tag->GetPlayCount()); return true; } break; case MUSICPLAYER_LASTPLAYED: case LISTITEM_LASTPLAYED: { const CDateTime& dateTime = tag->GetLastPlayed(); if (dateTime.IsValid()) { value = dateTime.GetAsLocalizedDate(); return true; } break; } case MUSICPLAYER_TRACK_NUMBER: case LISTITEM_TRACKNUMBER: if (tag->Loaded() && tag->GetTrackNumber() > 0) { value = StringUtils::Format("{:02}", tag->GetTrackNumber()); return true; } break; case MUSICPLAYER_DISC_NUMBER: case LISTITEM_DISC_NUMBER: if (tag->GetDiscNumber() > 0) { value = std::to_string(tag->GetDiscNumber()); return true; } break; case MUSICPLAYER_TOTALDISCS: case LISTITEM_TOTALDISCS: value = std::to_string(tag->GetTotalDiscs()); return true; case MUSICPLAYER_DISC_TITLE: case LISTITEM_DISC_TITLE: value = tag->GetDiscSubtitle(); return true; case MUSICPLAYER_ARTIST: case LISTITEM_ARTIST: value = tag->GetArtistString(); return true; case MUSICPLAYER_ALBUM_ARTIST: case LISTITEM_ALBUM_ARTIST: value = tag->GetAlbumArtistString(); return true; case MUSICPLAYER_CONTRIBUTORS: case LISTITEM_CONTRIBUTORS: if (tag->HasContributors()) { value = tag->GetContributorsText(); return true; } break; case MUSICPLAYER_CONTRIBUTOR_AND_ROLE: case LISTITEM_CONTRIBUTOR_AND_ROLE: if (tag->HasContributors()) { value = tag->GetContributorsAndRolesText(); return true; } break; case MUSICPLAYER_ALBUM: case LISTITEM_ALBUM: value = tag->GetAlbum(); return true; case MUSICPLAYER_YEAR: case LISTITEM_YEAR: value = tag->GetYearString(); return true; case MUSICPLAYER_GENRE: case LISTITEM_GENRE: value = StringUtils::Join(tag->GetGenre(), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator); return true; case MUSICPLAYER_LYRICS: value = tag->GetLyrics(); return true; case MUSICPLAYER_RATING: case LISTITEM_RATING: { float rating = tag->GetRating(); if (rating > 0.f) { value = StringUtils::FormatNumber(rating); return true; } break; } case MUSICPLAYER_RATING_AND_VOTES: case LISTITEM_RATING_AND_VOTES: { float rating = tag->GetRating(); if (rating > 0.f) { int votes = tag->GetVotes(); if (votes <= 0) value = StringUtils::FormatNumber(rating); else value = StringUtils::Format(g_localizeStrings.Get(20350), StringUtils::FormatNumber(rating), StringUtils::FormatNumber(votes)); return true; } break; } case MUSICPLAYER_USER_RATING: case LISTITEM_USER_RATING: if (tag->GetUserrating() > 0) { value = std::to_string(tag->GetUserrating()); return true; } break; case MUSICPLAYER_COMMENT: case LISTITEM_COMMENT: value = tag->GetComment(); return true; case MUSICPLAYER_MOOD: case LISTITEM_MOOD: value = tag->GetMood(); return true; case LISTITEM_DBTYPE: value = tag->GetType(); return true; case MUSICPLAYER_DBID: case LISTITEM_DBID: { int dbId = tag->GetDatabaseId(); if (dbId > -1) { value = std::to_string(dbId); return true; } break; } case PLAYER_DURATION: { const auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); if (!appPlayer->IsPlayingAudio()) break; } [[fallthrough]]; case MUSICPLAYER_DURATION: case LISTITEM_DURATION: { int iDuration = tag->GetDuration(); if (iDuration > 0) { value = StringUtils::SecondsToTimeString(iDuration, static_cast(info.m_info == LISTITEM_DURATION ? info.GetData4() : info.GetData1())); return true; } break; } case MUSICPLAYER_BPM: case LISTITEM_BPM: if (tag->GetBPM() > 0) { value = std::to_string(tag->GetBPM()); return true; } break; case MUSICPLAYER_STATIONNAME: // This property can be used for example by addons to enforce/override the station name. value = item->GetProperty("StationName").asString(); if (value.empty()) value = tag->GetStationName(); return true; ///////////////////////////////////////////////////////////////////////////////////////////// // LISTITEM_* ///////////////////////////////////////////////////////////////////////////////////////////// case LISTITEM_PROPERTY: if (StringUtils::StartsWithNoCase(info.GetData3(), "Role.")) { // "Role.xxxx" properties are held in music tag std::string property = info.GetData3(); property.erase(0, 5); //Remove Role. value = tag->GetArtistStringForRole(property); return true; } break; case LISTITEM_VOTES: value = StringUtils::FormatNumber(tag->GetVotes()); return true; case MUSICPLAYER_ORIGINALDATE: case LISTITEM_ORIGINALDATE: { value = tag->GetOriginalDate(); if (!CServiceBroker::GetSettingsComponent() ->GetAdvancedSettings() ->m_bMusicLibraryUseISODates) value = StringUtils::ISODateToLocalizedDate(value); return true; } case MUSICPLAYER_RELEASEDATE: case LISTITEM_RELEASEDATE: { value = tag->GetReleaseDate(); if (!CServiceBroker::GetSettingsComponent() ->GetAdvancedSettings() ->m_bMusicLibraryUseISODates) value = StringUtils::ISODateToLocalizedDate(value); return true; } break; case LISTITEM_BITRATE: { int BitRate = tag->GetBitRate(); if (BitRate > 0) { value = std::to_string(BitRate); return true; } break; } case LISTITEM_SAMPLERATE: { int sampleRate = tag->GetSampleRate(); if (sampleRate > 0) { value = StringUtils::Format("{:.5}", static_cast(sampleRate) / 1000.0); return true; } break; } case LISTITEM_MUSICCHANNELS: { int channels = tag->GetNoOfChannels(); if (channels > 0) { value = std::to_string(channels); return true; } break; } case LISTITEM_ALBUMSTATUS: value = tag->GetAlbumReleaseStatus(); return true; case LISTITEM_FILENAME: case LISTITEM_FILE_EXTENSION: if (item->IsMusicDb()) value = URIUtils::GetFileName(tag->GetURL()); else if (item->HasVideoInfoTag()) // special handling for music videos, which have both a videotag and a musictag break; else value = URIUtils::GetFileName(item->GetPath()); if (info.m_info == LISTITEM_FILE_EXTENSION) { std::string strExtension = URIUtils::GetExtension(value); value = StringUtils::TrimLeft(strExtension, "."); } return true; case LISTITEM_FOLDERNAME: case LISTITEM_PATH: if (item->IsMusicDb()) value = URIUtils::GetDirectory(tag->GetURL()); else if (item->HasVideoInfoTag()) // special handling for music videos, which have both a videotag and a musictag break; else URIUtils::GetParentPath(item->GetPath(), value); value = CURL(value).GetWithoutUserDetails(); if (info.m_info == LISTITEM_FOLDERNAME) { URIUtils::RemoveSlashAtEnd(value); value = URIUtils::GetFileName(value); } return true; case LISTITEM_FILENAME_AND_PATH: if (item->IsMusicDb()) value = tag->GetURL(); else if (item->HasVideoInfoTag()) // special handling for music videos, which have both a videotag and a musictag break; else value = item->GetPath(); value = CURL(value).GetWithoutUserDetails(); return true; case LISTITEM_DATE_ADDED: if (tag->GetDateAdded().IsValid()) { value = tag->GetDateAdded().GetAsLocalizedDate(); return true; } break; } } switch (info.m_info) { /////////////////////////////////////////////////////////////////////////////////////////////// // MUSICPLAYER_* /////////////////////////////////////////////////////////////////////////////////////////////// case MUSICPLAYER_PROPERTY: if (StringUtils::StartsWithNoCase(info.GetData3(), "Role.") && item->HasMusicInfoTag()) { // "Role.xxxx" properties are held in music tag std::string property = info.GetData3(); property.erase(0, 5); //Remove Role. value = item->GetMusicInfoTag()->GetArtistStringForRole(property); return true; } value = item->GetProperty(info.GetData3()).asString(); return true; case MUSICPLAYER_PLAYLISTLEN: if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC) { value = GUIINFO::GetPlaylistLabel(PLAYLIST_LENGTH); return true; } break; case MUSICPLAYER_PLAYLISTPOS: if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC) { value = GUIINFO::GetPlaylistLabel(PLAYLIST_POSITION); return true; } break; case MUSICPLAYER_COVER: { const auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); if (appPlayer->IsPlayingAudio()) { if (fallback) *fallback = "DefaultAlbumCover.png"; value = item->HasArt("thumb") ? item->GetArt("thumb") : "DefaultAlbumCover.png"; return true; } break; } case MUSICPLAYER_BITRATE: { int iBitrate = m_audioInfo.bitrate; if (iBitrate > 0) { value = std::to_string(std::lrint(static_cast(iBitrate) / 1000.0)); return true; } break; } case MUSICPLAYER_CHANNELS: { int iChannels = m_audioInfo.channels; if (iChannels > 0) { value = std::to_string(iChannels); return true; } break; } case MUSICPLAYER_BITSPERSAMPLE: { int iBPS = m_audioInfo.bitspersample; if (iBPS > 0) { value = std::to_string(iBPS); return true; } break; } case MUSICPLAYER_SAMPLERATE: { int iSamplerate = m_audioInfo.samplerate; if (iSamplerate > 0) { value = StringUtils::Format("{:.5}", static_cast(iSamplerate) / 1000.0); return true; } break; } case MUSICPLAYER_CODEC: value = m_audioInfo.codecName; return true; } /////////////////////////////////////////////////////////////////////////////////////////////// // MUSICPM_* /////////////////////////////////////////////////////////////////////////////////////////////// if (GetPartyModeLabel(value, info)) return true; return false; } bool CMusicGUIInfo::GetPartyModeLabel(std::string& value, const CGUIInfo &info) const { int iSongs = -1; switch (info.m_info) { case MUSICPM_SONGSPLAYED: iSongs = g_partyModeManager.GetSongsPlayed(); break; case MUSICPM_MATCHINGSONGS: iSongs = g_partyModeManager.GetMatchingSongs(); break; case MUSICPM_MATCHINGSONGSPICKED: iSongs = g_partyModeManager.GetMatchingSongsPicked(); break; case MUSICPM_MATCHINGSONGSLEFT: iSongs = g_partyModeManager.GetMatchingSongsLeft(); break; case MUSICPM_RELAXEDSONGSPICKED: iSongs = g_partyModeManager.GetRelaxedSongs(); break; case MUSICPM_RANDOMSONGSPICKED: iSongs = g_partyModeManager.GetRandomSongs(); break; } if (iSongs >= 0) { value = std::to_string(iSongs); return true; } return false; } bool CMusicGUIInfo::GetPlaylistInfo(std::string& value, const CGUIInfo &info) const { const PLAYLIST::CPlayList& playlist = CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::TYPE_MUSIC); if (playlist.size() < 1) return false; int index = info.GetData2(); if (info.GetData1() == 1) { // relative index (requires current playlist is TYPE_MUSIC) if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() != PLAYLIST::TYPE_MUSIC) return false; index = CServiceBroker::GetPlaylistPlayer().GetNextSong(index); } if (index < 0 || index >= playlist.size()) return false; const CFileItemPtr playlistItem = playlist[index]; if (playlistItem->HasMusicInfoTag() && !playlistItem->GetMusicInfoTag()->Loaded()) { playlistItem->LoadMusicTag(); playlistItem->GetMusicInfoTag()->SetLoaded(); } // try to set a thumbnail if (!playlistItem->HasArt("thumb")) { CMusicThumbLoader loader; loader.LoadItem(playlistItem.get()); // still no thumb? then just the set the default cover if (!playlistItem->HasArt("thumb")) playlistItem->SetArt("thumb", "DefaultAlbumCover.png"); } if (info.m_info == MUSICPLAYER_PLAYLISTPOS) { value = std::to_string(index + 1); return true; } else if (info.m_info == MUSICPLAYER_COVER) { value = playlistItem->GetArt("thumb"); return true; } return GetLabel(value, playlistItem.get(), 0, CGUIInfo(info.m_info), nullptr); } bool CMusicGUIInfo::GetFallbackLabel(std::string& value, const CFileItem* item, int contextWindow, const CGUIInfo& info, std::string* fallback) { // No fallback for musicplayer "offset" and "position" info labels if (info.GetData1() && ((info.m_info >= MUSICPLAYER_OFFSET_POSITION_FIRST && info.m_info <= MUSICPLAYER_OFFSET_POSITION_LAST) || (info.m_info >= PLAYER_OFFSET_POSITION_FIRST && info.m_info <= PLAYER_OFFSET_POSITION_LAST))) return false; const CMusicInfoTag* tag = item->GetMusicInfoTag(); if (tag) { switch (info.m_info) { ///////////////////////////////////////////////////////////////////////////////////////////// // MUSICPLAYER_* ///////////////////////////////////////////////////////////////////////////////////////////// case MUSICPLAYER_TITLE: value = item->GetLabel(); if (value.empty()) value = CUtil::GetTitleFromPath(item->GetPath()); return true; default: break; } } return false; } bool CMusicGUIInfo::GetInt(int& value, const CGUIListItem *gitem, int contextWindow, const CGUIInfo &info) const { return false; } bool CMusicGUIInfo::GetBool(bool& value, const CGUIListItem *gitem, int contextWindow, const CGUIInfo &info) const { const CFileItem* item = static_cast(gitem); const CMusicInfoTag* tag = item->GetMusicInfoTag(); switch (info.m_info) { /////////////////////////////////////////////////////////////////////////////////////////////// // MUSICPLAYER_* /////////////////////////////////////////////////////////////////////////////////////////////// case MUSICPLAYER_CONTENT: value = StringUtils::EqualsNoCase(info.GetData3(), "files"); return value; // if no match for this provider, other providers shall be asked. case MUSICPLAYER_HASPREVIOUS: // requires current playlist be TYPE_MUSIC if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC) { value = (CServiceBroker::GetPlaylistPlayer().GetCurrentSong() > 0); // not first song return true; } break; case MUSICPLAYER_HASNEXT: // requires current playlist be TYPE_MUSIC if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC) { value = (CServiceBroker::GetPlaylistPlayer().GetCurrentSong() < (CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::TYPE_MUSIC).size() - 1)); // not last song return true; } break; case MUSICPLAYER_PLAYLISTPLAYING: { const auto& components = CServiceBroker::GetAppComponents(); const auto appPlayer = components.GetComponent(); if (appPlayer->IsPlayingAudio() && CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC) { value = true; return true; } break; } case MUSICPLAYER_EXISTS: { int index = info.GetData2(); if (info.GetData1() == 1) { // relative index if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() != PLAYLIST::TYPE_MUSIC) { value = false; return true; } index += CServiceBroker::GetPlaylistPlayer().GetCurrentSong(); } value = (index >= 0 && index < CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::TYPE_MUSIC).size()); return true; } case MUSICPLAYER_ISMULTIDISC: if (tag) { value = (item->GetMusicInfoTag()->GetTotalDiscs() > 1); return true; } break; /////////////////////////////////////////////////////////////////////////////////////////////// // MUSICPM_* /////////////////////////////////////////////////////////////////////////////////////////////// case MUSICPM_ENABLED: value = g_partyModeManager.IsEnabled(); return true; /////////////////////////////////////////////////////////////////////////////////////////////// // LISTITEM_* /////////////////////////////////////////////////////////////////////////////////////////////// case LISTITEM_IS_BOXSET: if (tag) { value = tag->GetBoxset() == true; return true; } break; } return false; }