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.
9 #include "MusicThumbLoader.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"
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();
36 CThumbLoader::OnLoaderStart();
39 void CMusicThumbLoader::OnLoaderFinish()
41 m_musicDatabase
->Close();
43 CThumbLoader::OnLoaderFinish();
46 bool CMusicThumbLoader::LoadItem(CFileItem
* pItem
)
48 bool result
= LoadItemCached(pItem
);
49 result
|= LoadItemLookup(pItem
);
54 bool CMusicThumbLoader::LoadItemCached(CFileItem
* pItem
)
56 if (pItem
->m_bIsShareOrDrive
)
59 if (pItem
->HasMusicInfoTag() && !pItem
->GetProperty("libraryartfilled").asBoolean())
61 if (FillLibraryArt(*pItem
))
64 if (pItem
->GetMusicInfoTag()->GetType() == MediaTypeArtist
)
65 return false; // No fallback
68 if (pItem
->HasVideoInfoTag() && !pItem
->HasArt("thumb"))
70 CVideoThumbLoader loader
;
71 if (loader
.LoadItemCached(pItem
))
75 // Fallback to folder thumb when path has one cached
76 if (!pItem
->HasArt("thumb"))
78 std::string art
= GetCachedImage(*pItem
, "thumb");
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");
91 pItem
->SetArt("fanart", art
);
98 bool CMusicThumbLoader::LoadItemLookup(CFileItem
* pItem
)
100 if (pItem
->m_bIsShareOrDrive
)
103 if (pItem
->HasMusicInfoTag() && pItem
->GetMusicInfoTag()->GetType() == MediaTypeArtist
) // No fallback for artist
106 if (pItem
->HasVideoInfoTag())
108 CVideoThumbLoader loader
;
109 if (loader
.LoadItemLookup(pItem
))
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
);
128 // Check for user thumbs
129 FillThumb(*pItem
, true);
136 bool CMusicThumbLoader::FillThumb(CFileItem
&item
, bool folderThumbs
/* = true */)
138 if (item
.HasArt("thumb"))
140 std::string thumb
= GetCachedImage(item
, "thumb");
143 thumb
= item
.GetUserMusicThumb(false, folderThumbs
);
145 SetCachedImage(item
, "thumb", thumb
);
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
);
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.
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();
197 for (const auto& artistCredit
: song
.artistCredits
)
199 int idArtist
= m_musicDatabase
->GetArtistByName(artistCredit
.GetArtist());
202 std::vector
<ArtForThumbLoader
> artistart
;
203 if (m_musicDatabase
->GetArtForItem(-1, -1, idArtist
, true, artistart
))
205 for (auto& artitem
: artistart
)
208 artitem
.prefix
= StringUtils::Format("artist{}", iOrder
);
210 artitem
.prefix
= "artist";
212 art
.insert(art
.end(), artistart
.begin(), artistart
.end());
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
223 album
.SetArtistCredits(tag
.GetAlbumArtist(), tag
.GetMusicBrainzAlbumArtistHints(), tag
.GetMusicBrainzAlbumArtistID(),
224 tag
.GetArtist(), tag
.GetMusicBrainzArtistHints(), tag
.GetMusicBrainzArtistID());
227 for (const auto& artistCredit
: album
.artistCredits
)
229 int idArtist
= m_musicDatabase
->GetArtistByName(artistCredit
.GetArtist());
232 std::vector
<ArtForThumbLoader
> artistart
;
233 if (m_musicDatabase
->GetArtForItem(-1, -1, idArtist
, true, artistart
))
235 for (auto& artitem
: artistart
)
238 artitem
.prefix
= StringUtils::Format("albumartist{}", iOrder
);
240 artitem
.prefix
= "albumartist";
242 art
.insert(art
.end(), artistart
.begin(), artistart
.end());
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();
270 std::string fanartfallback
;
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
;
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
));
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
;
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
;
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");
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);