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 "TextureDatabase.h"
13 #include "music/infoscanner/MusicInfoScanner.h"
14 #include "music/tags/MusicInfoTag.h"
15 #include "music/tags/MusicInfoTagLoaderFactory.h"
16 #include "utils/StringUtils.h"
17 #include "video/VideoThumbLoader.h"
21 using namespace MUSIC_INFO
;
23 CMusicThumbLoader::CMusicThumbLoader() : CThumbLoader()
25 m_musicDatabase
= new CMusicDatabase
;
28 CMusicThumbLoader::~CMusicThumbLoader()
30 delete m_musicDatabase
;
33 void CMusicThumbLoader::OnLoaderStart()
35 m_musicDatabase
->Open();
37 CThumbLoader::OnLoaderStart();
40 void CMusicThumbLoader::OnLoaderFinish()
42 m_musicDatabase
->Close();
44 CThumbLoader::OnLoaderFinish();
47 bool CMusicThumbLoader::LoadItem(CFileItem
* pItem
)
49 bool result
= LoadItemCached(pItem
);
50 result
|= LoadItemLookup(pItem
);
55 bool CMusicThumbLoader::LoadItemCached(CFileItem
* pItem
)
57 if (pItem
->m_bIsShareOrDrive
)
60 if (pItem
->HasMusicInfoTag() && !pItem
->GetProperty("libraryartfilled").asBoolean())
62 if (FillLibraryArt(*pItem
))
65 if (pItem
->GetMusicInfoTag()->GetType() == MediaTypeArtist
)
66 return false; // No fallback
69 if (pItem
->HasVideoInfoTag() && !pItem
->HasArt("thumb"))
71 CVideoThumbLoader loader
;
72 if (loader
.LoadItemCached(pItem
))
76 // Fallback to folder thumb when path has one cached
77 if (!pItem
->HasArt("thumb"))
79 std::string art
= GetCachedImage(*pItem
, "thumb");
81 pItem
->SetArt("thumb", art
);
84 // Fallback to folder fanart when path has one cached
85 //! @todo Remove as "fanart" is never been cached for music folders (only for
86 // artists) or start caching fanart for folders?
87 if (!pItem
->HasArt("fanart"))
89 std::string art
= GetCachedImage(*pItem
, "fanart");
92 pItem
->SetArt("fanart", art
);
99 bool CMusicThumbLoader::LoadItemLookup(CFileItem
* pItem
)
101 if (pItem
->m_bIsShareOrDrive
)
104 if (pItem
->HasMusicInfoTag() && pItem
->GetMusicInfoTag()->GetType() == MediaTypeArtist
) // No fallback for artist
107 if (pItem
->HasVideoInfoTag())
109 CVideoThumbLoader loader
;
110 if (loader
.LoadItemLookup(pItem
))
114 if (!pItem
->HasArt("thumb"))
116 // Look for embedded art
117 if (pItem
->HasMusicInfoTag() && !pItem
->GetMusicInfoTag()->GetCoverArtInfo().Empty())
119 // The item has got embedded art but user thumbs overrule, so check for those first
120 if (!FillThumb(*pItem
, false)) // Check for user thumbs but ignore folder thumbs
122 // No user thumb, use embedded art
123 std::string thumb
= CTextureUtils::GetWrappedImageURL(pItem
->GetPath(), "music");
124 pItem
->SetArt("thumb", thumb
);
129 // Check for user thumbs
130 FillThumb(*pItem
, true);
137 bool CMusicThumbLoader::FillThumb(CFileItem
&item
, bool folderThumbs
/* = true */)
139 if (item
.HasArt("thumb"))
141 std::string thumb
= GetCachedImage(item
, "thumb");
144 thumb
= item
.GetUserMusicThumb(false, folderThumbs
);
146 SetCachedImage(item
, "thumb", thumb
);
149 item
.SetArt("thumb", thumb
);
150 return !thumb
.empty();
153 bool CMusicThumbLoader::FillLibraryArt(CFileItem
&item
)
155 /* Called for any item with MusicInfoTag and no art.
156 Items on Genres, Sources and Roles nodes have ID (although items on Years
157 node do not) so check for song/album/artist specifically.
158 Non-library songs (file view) can also have MusicInfoTag but no ID or type
160 bool artfound(false);
161 std::vector
<ArtForThumbLoader
> art
;
162 CMusicInfoTag
&tag
= *item
.GetMusicInfoTag();
163 if (tag
.GetDatabaseId() > -1 &&
164 (tag
.GetType() == MediaTypeSong
|| tag
.GetType() == MediaTypeAlbum
||
165 tag
.GetType() == MediaTypeArtist
))
167 // Item in music library, fetch the art
168 m_musicDatabase
->Open();
169 if (tag
.GetType() == MediaTypeSong
)
170 artfound
= m_musicDatabase
->GetArtForItem(tag
.GetDatabaseId(), tag
.GetAlbumId(), -1, false, art
);
171 else if (tag
.GetType() == MediaTypeAlbum
)
172 artfound
= m_musicDatabase
->GetArtForItem(-1, tag
.GetDatabaseId(), -1, false, art
);
174 artfound
= m_musicDatabase
->GetArtForItem(-1, -1, tag
.GetDatabaseId(), true, art
);
176 m_musicDatabase
->Close();
178 else if (!tag
.GetArtist().empty() &&
179 (tag
.GetType() == MediaTypeNone
|| tag
.GetType() == MediaTypeSong
))
182 Could be non-library song - has musictag but no ID or type (may have
183 thumb already). Try to fetch both song artist(s) and album artist(s) art by
184 artist name, e.g. "artist.thumb", "artist.fanart", "artist.clearlogo",
185 "artist.banner", "artist1.thumb", "artist1.fanart", "artist1.clearlogo",
186 "artist1.banner", "albumartist.thumb", "albumartist.fanart" etc.
187 Set fanart as fallback.
190 // Try to split song artist names (various tags) into artist credits
191 song
.SetArtistCredits(tag
.GetArtist(), tag
.GetMusicBrainzArtistHints(), tag
.GetMusicBrainzArtistID());
192 if (!song
.artistCredits
.empty())
194 tag
.SetType(MediaTypeSong
); // Makes "Information" context menu visible
195 m_musicDatabase
->Open();
198 for (const auto& artistCredit
: song
.artistCredits
)
200 int idArtist
= m_musicDatabase
->GetArtistByName(artistCredit
.GetArtist());
203 std::vector
<ArtForThumbLoader
> artistart
;
204 if (m_musicDatabase
->GetArtForItem(-1, -1, idArtist
, true, artistart
))
206 for (auto& artitem
: artistart
)
209 artitem
.prefix
= StringUtils::Format("artist{}", iOrder
);
211 artitem
.prefix
= "artist";
213 art
.insert(art
.end(), artistart
.begin(), artistart
.end());
219 if (!tag
.GetAlbumArtist().empty() && tag
.GetArtistString().compare(tag
.GetAlbumArtistString()) != 0)
221 // Split song artist names correctly into artist credits from various tag
222 // arrays, inc. fallback to song artist names
224 album
.SetArtistCredits(tag
.GetAlbumArtist(), tag
.GetMusicBrainzAlbumArtistHints(), tag
.GetMusicBrainzAlbumArtistID(),
225 tag
.GetArtist(), tag
.GetMusicBrainzArtistHints(), tag
.GetMusicBrainzArtistID());
228 for (const auto& artistCredit
: album
.artistCredits
)
230 int idArtist
= m_musicDatabase
->GetArtistByName(artistCredit
.GetArtist());
233 std::vector
<ArtForThumbLoader
> artistart
;
234 if (m_musicDatabase
->GetArtForItem(-1, -1, idArtist
, true, artistart
))
236 for (auto& artitem
: artistart
)
239 artitem
.prefix
= StringUtils::Format("albumartist{}", iOrder
);
241 artitem
.prefix
= "albumartist";
243 art
.insert(art
.end(), artistart
.begin(), artistart
.end());
251 // Replicate the artist art as album artist art
252 std::vector
<ArtForThumbLoader
> artistart
;
253 for (const auto& artitem
: art
)
255 ArtForThumbLoader newart
;
256 newart
.artType
= artitem
.artType
;
257 newart
.mediaType
= artitem
.mediaType
;
258 newart
.prefix
= "album" + artitem
.prefix
;
259 newart
.url
= artitem
.url
;
260 artistart
.emplace_back(newart
);
262 art
.insert(art
.end(), artistart
.begin(), artistart
.end());
264 artfound
= !art
.empty();
265 m_musicDatabase
->Close();
271 std::string fanartfallback
;
273 std::map
<std::string
, std::string
> artmap
;
274 std::map
<std::string
, std::string
> discartmap
;
275 for (auto artitem
: art
)
277 /* Add art to artmap, naming according to media type.
278 For example: artists have "thumb", "fanart", "poster" etc.,
279 albums have "thumb", "artist.thumb", "artist.fanart",... "artist1.thumb", "artist1.fanart" etc.,
280 songs have "thumb", "album.thumb", "artist.thumb", "albumartist.thumb", "albumartist1.thumb" etc.
282 if (tag
.GetType() == artitem
.mediaType
)
283 artname
= artitem
.artType
;
284 else if (artitem
.prefix
.empty())
285 artname
= artitem
.mediaType
+ "." + artitem
.artType
;
288 if (tag
.GetType() == MediaTypeAlbum
)
289 StringUtils::Replace(artitem
.prefix
, "albumartist", "artist");
290 artname
= artitem
.prefix
+ "." + artitem
.artType
;
293 // Pull out album art for this specific disc e.g. "thumb2", skip art for other discs
294 if (artitem
.mediaType
== MediaTypeAlbum
&& tag
.GetDiscNumber() > 0)
296 // Find any trailing digits
297 size_t startnum
= artitem
.artType
.find_last_not_of("0123456789");
298 std::string digits
= artitem
.artType
.substr(startnum
+ 1);
299 int num
= atoi(digits
.c_str());
300 if (num
> 0 && startnum
< artitem
.artType
.size())
302 if (num
== tag
.GetDiscNumber())
303 discartmap
.insert(std::make_pair(artitem
.artType
.substr(0, startnum
+ 1), artitem
.url
));
308 artmap
.insert(std::make_pair(artname
, artitem
.url
));
310 // Add fallback art for "thumb" and "fanart" art types only
311 // Set album thumb as the fallback used when song thumb is missing
312 if (tag
.GetType() == MediaTypeSong
&& artitem
.mediaType
== MediaTypeAlbum
&&
313 artitem
.artType
== "thumb")
315 item
.SetArtFallback(artitem
.artType
, artname
);
318 // For albums and songs set fallback fanart from the artist.
319 // For songs prefer primary song artist over primary albumartist fanart as fallback fanart
320 if (artitem
.prefix
== "artist" && artitem
.artType
== "fanart")
321 fanartfallback
= artname
;
322 if (artitem
.prefix
== "albumartist" && artitem
.artType
== "fanart" && fanartfallback
.empty())
323 fanartfallback
= artname
;
325 if (!fanartfallback
.empty())
326 item
.SetArtFallback("fanart", fanartfallback
);
328 // Process specific disc art when we have some
329 for (const auto& discart
: discartmap
)
331 std::map
<std::string
, std::string
>::iterator it
;
332 if (tag
.GetType() == MediaTypeAlbum
)
334 // Insert or replace album art with specific disc art
335 it
= artmap
.find(discart
.first
);
336 if (it
!= artmap
.end())
337 it
->second
= discart
.second
;
339 artmap
.insert(discart
);
341 else if (tag
.GetType() == MediaTypeSong
)
343 // Use disc thumb rather than album as fallback for song thumb
344 // (Fallback approach is used to fill missing thumbs).
345 if (discart
.first
== "thumb")
347 it
= artmap
.find("album.thumb");
348 if (it
!= artmap
.end())
349 // Replace "album.thumb" already set as fallback
350 it
->second
= discart
.second
;
353 // Insert thumb for album and set as fallback
354 artmap
.insert(std::make_pair("album.thumb", discart
.second
));
355 item
.SetArtFallback("thumb", "album.thumb");
360 // Apply disc art as song art when not have that type (fallback does not apply).
361 // Art of other types could been set via JSON, or in future read from metadata
362 it
= artmap
.find(discart
.first
);
363 if (it
== artmap
.end())
364 artmap
.insert(discart
);
369 item
.AppendArt(artmap
);
372 item
.SetProperty("libraryartfilled", true);
376 bool CMusicThumbLoader::GetEmbeddedThumb(const std::string
&path
, EmbeddedArt
&art
)
378 CFileItem
item(path
, false);
379 std::unique_ptr
<IMusicInfoTagLoader
> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(item
));
381 if (nullptr != pLoader
)
382 pLoader
->Load(path
, tag
, &art
);