Merge pull request #26350 from jjd-uk/estuary_media_align
[xbmc.git] / xbmc / music / MusicThumbLoader.cpp
blob0ba67f0e8af562637ba3910099c8b779f6fd4b02
1 /*
2 * Copyright (C) 2012-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 "MusicThumbLoader.h"
11 #include "FileItem.h"
12 #include "imagefiles/ImageFileURL.h"
13 #include "music/infoscanner/MusicInfoScanner.h"
14 #include "music/tags/MusicInfoTag.h"
15 #include "utils/StringUtils.h"
16 #include "video/VideoThumbLoader.h"
18 #include <utility>
20 using namespace MUSIC_INFO;
22 CMusicThumbLoader::CMusicThumbLoader() : CThumbLoader()
24 m_musicDatabase = new CMusicDatabase;
27 CMusicThumbLoader::~CMusicThumbLoader()
29 delete m_musicDatabase;
32 void CMusicThumbLoader::OnLoaderStart()
34 m_musicDatabase->Open();
35 m_albumArt.clear();
36 CThumbLoader::OnLoaderStart();
39 void CMusicThumbLoader::OnLoaderFinish()
41 m_musicDatabase->Close();
42 m_albumArt.clear();
43 CThumbLoader::OnLoaderFinish();
46 bool CMusicThumbLoader::LoadItem(CFileItem* pItem)
48 bool result = LoadItemCached(pItem);
49 result |= LoadItemLookup(pItem);
51 return result;
54 bool CMusicThumbLoader::LoadItemCached(CFileItem* pItem)
56 if (pItem->m_bIsShareOrDrive)
57 return false;
59 if (pItem->HasMusicInfoTag() && !pItem->GetProperty("libraryartfilled").asBoolean())
61 if (FillLibraryArt(*pItem))
62 return true;
64 if (pItem->GetMusicInfoTag()->GetType() == MediaTypeArtist)
65 return false; // No fallback
68 if (pItem->HasVideoInfoTag() && !pItem->HasArt("thumb"))
69 { // music video
70 CVideoThumbLoader loader;
71 if (loader.LoadItemCached(pItem))
72 return true;
75 // Fallback to folder thumb when path has one cached
76 if (!pItem->HasArt("thumb"))
78 std::string art = GetCachedImage(*pItem, "thumb");
79 if (!art.empty())
80 pItem->SetArt("thumb", art);
83 // Fallback to folder fanart when path has one cached
84 //! @todo Remove as "fanart" is never been cached for music folders (only for
85 // artists) or start caching fanart for folders?
86 if (!pItem->HasArt("fanart"))
88 std::string art = GetCachedImage(*pItem, "fanart");
89 if (!art.empty())
91 pItem->SetArt("fanart", art);
95 return false;
98 bool CMusicThumbLoader::LoadItemLookup(CFileItem* pItem)
100 if (pItem->m_bIsShareOrDrive)
101 return false;
103 if (pItem->HasMusicInfoTag() && pItem->GetMusicInfoTag()->GetType() == MediaTypeArtist) // No fallback for artist
104 return false;
106 if (pItem->HasVideoInfoTag())
107 { // music video
108 CVideoThumbLoader loader;
109 if (loader.LoadItemLookup(pItem))
110 return true;
113 if (!pItem->HasArt("thumb"))
115 // Look for embedded art
116 if (pItem->HasMusicInfoTag() && !pItem->GetMusicInfoTag()->GetCoverArtInfo().Empty())
118 // The item has got embedded art but user thumbs overrule, so check for those first
119 if (!FillThumb(*pItem, false)) // Check for user thumbs but ignore folder thumbs
121 // No user thumb, use embedded art
122 std::string thumb = IMAGE_FILES::URLFromFile(pItem->GetPath(), "music");
123 pItem->SetArt("thumb", thumb);
126 else
128 // Check for user thumbs
129 FillThumb(*pItem, true);
133 return true;
136 bool CMusicThumbLoader::FillThumb(CFileItem &item, bool folderThumbs /* = true */)
138 if (item.HasArt("thumb"))
139 return true;
140 std::string thumb = GetCachedImage(item, "thumb");
141 if (thumb.empty())
143 thumb = item.GetUserMusicThumb(false, folderThumbs);
144 if (!thumb.empty())
145 SetCachedImage(item, "thumb", thumb);
147 if (!thumb.empty())
148 item.SetArt("thumb", thumb);
149 return !thumb.empty();
152 bool CMusicThumbLoader::FillLibraryArt(CFileItem &item)
154 /* Called for any item with MusicInfoTag and no art.
155 Items on Genres, Sources and Roles nodes have ID (although items on Years
156 node do not) so check for song/album/artist specifically.
157 Non-library songs (file view) can also have MusicInfoTag but no ID or type
159 bool artfound(false);
160 std::vector<ArtForThumbLoader> art;
161 CMusicInfoTag &tag = *item.GetMusicInfoTag();
162 if (tag.GetDatabaseId() > -1 &&
163 (tag.GetType() == MediaTypeSong || tag.GetType() == MediaTypeAlbum ||
164 tag.GetType() == MediaTypeArtist))
166 // Item in music library, fetch the art
167 m_musicDatabase->Open();
168 if (tag.GetType() == MediaTypeSong)
169 artfound = m_musicDatabase->GetArtForItem(tag.GetDatabaseId(), tag.GetAlbumId(), -1, false, art);
170 else if (tag.GetType() == MediaTypeAlbum)
171 artfound = m_musicDatabase->GetArtForItem(-1, tag.GetDatabaseId(), -1, false, art);
172 else //Artist
173 artfound = m_musicDatabase->GetArtForItem(-1, -1, tag.GetDatabaseId(), true, art);
175 m_musicDatabase->Close();
177 else if (!tag.GetArtist().empty() &&
178 (tag.GetType() == MediaTypeNone || tag.GetType() == MediaTypeSong))
181 Could be non-library song - has musictag but no ID or type (may have
182 thumb already). Try to fetch both song artist(s) and album artist(s) art by
183 artist name, e.g. "artist.thumb", "artist.fanart", "artist.clearlogo",
184 "artist.banner", "artist1.thumb", "artist1.fanart", "artist1.clearlogo",
185 "artist1.banner", "albumartist.thumb", "albumartist.fanart" etc.
186 Set fanart as fallback.
188 CSong song;
189 // Try to split song artist names (various tags) into artist credits
190 song.SetArtistCredits(tag.GetArtist(), tag.GetMusicBrainzArtistHints(), tag.GetMusicBrainzArtistID());
191 if (!song.artistCredits.empty())
193 tag.SetType(MediaTypeSong); // Makes "Information" context menu visible
194 m_musicDatabase->Open();
195 int iOrder = 0;
196 // Song artist art
197 for (const auto& artistCredit : song.artistCredits)
199 int idArtist = m_musicDatabase->GetArtistByName(artistCredit.GetArtist());
200 if (idArtist > 0)
202 std::vector<ArtForThumbLoader> artistart;
203 if (m_musicDatabase->GetArtForItem(-1, -1, idArtist, true, artistart))
205 for (auto& artitem : artistart)
207 if (iOrder > 0)
208 artitem.prefix = StringUtils::Format("artist{}", iOrder);
209 else
210 artitem.prefix = "artist";
212 art.insert(art.end(), artistart.begin(), artistart.end());
215 ++iOrder;
217 // Album artist art
218 if (!tag.GetAlbumArtist().empty() && tag.GetArtistString().compare(tag.GetAlbumArtistString()) != 0)
220 // Split song artist names correctly into artist credits from various tag
221 // arrays, inc. fallback to song artist names
222 CAlbum album;
223 album.SetArtistCredits(tag.GetAlbumArtist(), tag.GetMusicBrainzAlbumArtistHints(), tag.GetMusicBrainzAlbumArtistID(),
224 tag.GetArtist(), tag.GetMusicBrainzArtistHints(), tag.GetMusicBrainzArtistID());
226 iOrder = 0;
227 for (const auto& artistCredit : album.artistCredits)
229 int idArtist = m_musicDatabase->GetArtistByName(artistCredit.GetArtist());
230 if (idArtist > 0)
232 std::vector<ArtForThumbLoader> artistart;
233 if (m_musicDatabase->GetArtForItem(-1, -1, idArtist, true, artistart))
235 for (auto& artitem : artistart)
237 if (iOrder > 0)
238 artitem.prefix = StringUtils::Format("albumartist{}", iOrder);
239 else
240 artitem.prefix = "albumartist";
242 art.insert(art.end(), artistart.begin(), artistart.end());
245 ++iOrder;
248 else
250 // Replicate the artist art as album artist art
251 std::vector<ArtForThumbLoader> artistart;
252 for (const auto& artitem : art)
254 ArtForThumbLoader newart;
255 newart.artType = artitem.artType;
256 newart.mediaType = artitem.mediaType;
257 newart.prefix = "album" + artitem.prefix;
258 newart.url = artitem.url;
259 artistart.emplace_back(newart);
261 art.insert(art.end(), artistart.begin(), artistart.end());
263 artfound = !art.empty();
264 m_musicDatabase->Close();
268 if (artfound)
270 std::string fanartfallback;
271 std::string artname;
272 std::map<std::string, std::string> artmap;
273 std::map<std::string, std::string> discartmap;
274 for (auto artitem : art)
276 /* Add art to artmap, naming according to media type.
277 For example: artists have "thumb", "fanart", "poster" etc.,
278 albums have "thumb", "artist.thumb", "artist.fanart",... "artist1.thumb", "artist1.fanart" etc.,
279 songs have "thumb", "album.thumb", "artist.thumb", "albumartist.thumb", "albumartist1.thumb" etc.
281 if (tag.GetType() == artitem.mediaType)
282 artname = artitem.artType;
283 else if (artitem.prefix.empty())
284 artname = artitem.mediaType + "." + artitem.artType;
285 else
287 if (tag.GetType() == MediaTypeAlbum)
288 StringUtils::Replace(artitem.prefix, "albumartist", "artist");
289 artname = artitem.prefix + "." + artitem.artType;
292 // Pull out album art for this specific disc e.g. "thumb2", skip art for other discs
293 if (artitem.mediaType == MediaTypeAlbum && tag.GetDiscNumber() > 0)
295 // Find any trailing digits
296 size_t startnum = artitem.artType.find_last_not_of("0123456789");
297 std::string digits = artitem.artType.substr(startnum + 1);
298 int num = atoi(digits.c_str());
299 if (num > 0 && startnum < artitem.artType.size())
301 if (num == tag.GetDiscNumber())
302 discartmap.insert(std::make_pair(artitem.artType.substr(0, startnum + 1), artitem.url));
303 continue;
307 artmap.insert(std::make_pair(artname, artitem.url));
309 // Add fallback art for "thumb" and "fanart" art types only
310 // Set album thumb as the fallback used when song thumb is missing
311 if (tag.GetType() == MediaTypeSong && artitem.mediaType == MediaTypeAlbum &&
312 artitem.artType == "thumb")
314 item.SetArtFallback(artitem.artType, artname);
317 // For albums and songs set fallback fanart from the artist.
318 // For songs prefer primary song artist over primary albumartist fanart as fallback fanart
319 if (artitem.prefix == "artist" && artitem.artType == "fanart")
320 fanartfallback = artname;
321 if (artitem.prefix == "albumartist" && artitem.artType == "fanart" && fanartfallback.empty())
322 fanartfallback = artname;
324 if (!fanartfallback.empty())
325 item.SetArtFallback("fanart", fanartfallback);
327 // Process specific disc art when we have some
328 for (const auto& discart : discartmap)
330 std::map<std::string, std::string>::iterator it;
331 if (tag.GetType() == MediaTypeAlbum)
333 // Insert or replace album art with specific disc art
334 it = artmap.find(discart.first);
335 if (it != artmap.end())
336 it->second = discart.second;
337 else
338 artmap.insert(discart);
340 else if (tag.GetType() == MediaTypeSong)
342 // Use disc thumb rather than album as fallback for song thumb
343 // (Fallback approach is used to fill missing thumbs).
344 if (discart.first == "thumb")
346 it = artmap.find("album.thumb");
347 if (it != artmap.end())
348 // Replace "album.thumb" already set as fallback
349 it->second = discart.second;
350 else
352 // Insert thumb for album and set as fallback
353 artmap.insert(std::make_pair("album.thumb", discart.second));
354 item.SetArtFallback("thumb", "album.thumb");
357 else
359 // Apply disc art as song art when not have that type (fallback does not apply).
360 // Art of other types could been set via JSON, or in future read from metadata
361 it = artmap.find(discart.first);
362 if (it == artmap.end())
363 artmap.insert(discart);
368 item.AppendArt(artmap);
369 item.SetProperty("libraryartfilled", true);
372 return artfound;