2 * Copyright (C) 2005-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 "MusicInfoLoader.h"
14 #include "MusicDatabase.h"
15 #include "MusicThumbLoader.h"
16 #include "ServiceBroker.h"
17 #include "filesystem/File.h"
18 #include "filesystem/MusicDatabaseDirectory/DirectoryNode.h"
19 #include "filesystem/MusicDatabaseDirectory/QueryParams.h"
20 #include "music/tags/MusicInfoTag.h"
21 #include "music/tags/MusicInfoTagLoaderFactory.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "utils/Archive.h"
25 #include "utils/StringUtils.h"
26 #include "utils/URIUtils.h"
27 #include "utils/log.h"
29 using namespace XFILE
;
30 using namespace MUSIC_INFO
;
32 // HACK until we make this threadable - specify 1 thread only for now
33 CMusicInfoLoader::CMusicInfoLoader()
34 : CBackgroundInfoLoader()
38 m_mapFileItems
= new CFileItemList
;
40 m_thumbLoader
= new CMusicThumbLoader();
43 CMusicInfoLoader::~CMusicInfoLoader()
46 delete m_mapFileItems
;
50 void CMusicInfoLoader::OnLoaderStart()
52 // Load previously cached items from HD
53 if (!m_strCacheFileName
.empty())
54 LoadCache(m_strCacheFileName
, *m_mapFileItems
);
57 m_mapFileItems
->SetPath(m_pVecItems
->GetPath());
58 m_mapFileItems
->Load();
59 m_mapFileItems
->SetFastLookup(true);
62 m_strPrevPath
.clear();
64 m_databaseHits
= m_tagReads
= 0;
66 if (m_pProgressCallback
)
67 m_pProgressCallback
->SetProgressMax(m_pVecItems
->GetFileCount());
69 m_musicDatabase
.Open();
72 m_thumbLoader
->OnLoaderStart();
75 bool CMusicInfoLoader::LoadAdditionalTagInfo(CFileItem
* pItem
)
77 if (!pItem
|| (pItem
->m_bIsFolder
&& !pItem
->IsAudio()) ||
78 pItem
->IsPlayList() || pItem
->IsNFO() || pItem
->IsInternetStream())
81 if (pItem
->GetProperty("hasfullmusictag") == "true")
82 return false; // already have the information
84 std::string
path(pItem
->GetPath());
85 // For songs in library set the (primary) song artist and album properties
86 // Use song Id (not path) as called for items from either library or file view,
87 // but could also be listitem with tag loaded by a script
88 if (pItem
->HasMusicInfoTag() &&
89 pItem
->GetMusicInfoTag()->GetType() == MediaTypeSong
&&
90 pItem
->GetMusicInfoTag()->GetDatabaseId() > 0)
92 CMusicDatabase database
;
94 // May already have song artist ids as item property set when data read from
95 // db, but check property is valid array (scripts could set item properties
96 // incorrectly), otherwise fetch artist using song id.
98 bool artistfound
= false;
99 if (pItem
->HasProperty("artistid") && pItem
->GetProperty("artistid").isArray())
101 CVariant::const_iterator_array varid
= pItem
->GetProperty("artistid").begin_array();
102 int idArtist
= static_cast<int>(varid
->asInteger());
103 artistfound
= database
.GetArtist(idArtist
, artist
, false);
106 artistfound
= database
.GetArtistFromSong(pItem
->GetMusicInfoTag()->GetDatabaseId(), artist
);
108 CMusicDatabase::SetPropertiesFromArtist(*pItem
, artist
);
110 // May already have album id, otherwise fetch album from song id
112 bool albumfound
= false;
113 int idAlbum
= pItem
->GetMusicInfoTag()->GetAlbumId();
115 albumfound
= database
.GetAlbum(idAlbum
, album
, false);
117 albumfound
= database
.GetAlbumFromSong(pItem
->GetMusicInfoTag()->GetDatabaseId(), album
);
119 CMusicDatabase::SetPropertiesFromAlbum(*pItem
, album
);
121 path
= pItem
->GetMusicInfoTag()->GetURL();
124 CLog::Log(LOGDEBUG
, "Loading additional tag info for file {}", path
);
126 // we load up the actual tag for this file in order to
127 // fetch the lyrics and add it to the current music info tag
128 CFileItem
tempItem(path
, false);
129 std::unique_ptr
<IMusicInfoTagLoader
> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(tempItem
));
130 if (nullptr != pLoader
)
133 pLoader
->Load(path
, tag
);
134 pItem
->GetMusicInfoTag()->SetLyrics(tag
.GetLyrics());
135 pItem
->SetProperty("hasfullmusictag", "true");
141 bool CMusicInfoLoader::LoadItem(CFileItem
* pItem
)
143 bool result
= LoadItemCached(pItem
);
144 result
|= LoadItemLookup(pItem
);
149 bool CMusicInfoLoader::LoadItemCached(CFileItem
* pItem
)
151 if ((pItem
->m_bIsFolder
&& !pItem
->IsAudio()) ||
152 pItem
->IsPlayList() || pItem
->IsSmartPlayList() ||
153 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newplaylist://") ||
154 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newsmartplaylist://") ||
155 pItem
->IsNFO() || (pItem
->IsInternetStream() && !pItem
->IsMusicDb()))
158 // Get thumb for item
159 m_thumbLoader
->LoadItem(pItem
);
164 bool CMusicInfoLoader::LoadItemLookup(CFileItem
* pItem
)
166 if (m_pProgressCallback
&& !pItem
->m_bIsFolder
)
167 m_pProgressCallback
->SetProgressAdvance();
169 if ((pItem
->m_bIsFolder
&& !pItem
->IsAudio()) || //
170 pItem
->IsPlayList() || pItem
->IsSmartPlayList() || //
171 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newplaylist://") || //
172 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newsmartplaylist://") || //
173 pItem
->IsNFO() || (pItem
->IsInternetStream() && !pItem
->IsMusicDb()))
176 if ((!pItem
->HasMusicInfoTag() || !pItem
->GetMusicInfoTag()->Loaded()) && pItem
->IsAudio())
178 // first check the cached item
179 CFileItemPtr mapItem
= (*m_mapFileItems
)[pItem
->GetPath()];
180 if (mapItem
&& mapItem
->m_dateTime
==pItem
->m_dateTime
&& mapItem
->HasMusicInfoTag() && mapItem
->GetMusicInfoTag()->Loaded())
181 { // Query map if we previously cached the file on HD
182 *pItem
->GetMusicInfoTag() = *mapItem
->GetMusicInfoTag();
183 if (mapItem
->HasArt("thumb"))
184 pItem
->SetArt("thumb", mapItem
->GetArt("thumb"));
188 std::string strPath
= URIUtils::GetDirectory(pItem
->GetPath());
189 URIUtils::AddSlashAtEnd(strPath
);
190 if (strPath
!=m_strPrevPath
)
192 // The item is from another directory as the last one,
193 // query the database for the new directory...
194 m_musicDatabase
.GetSongsByPath(strPath
, m_songsMap
);
199 This only loads the item with the song from the database when it maps to a single song,
200 it can not load song data for items with cuesheets that expand to multiple songs.
201 For songs from embedded or separate cuesheets strFileName is not unique, so the song map for
202 the path will have the list of songs from that file. But items with cuesheets are expanded
203 (replacing each item with items for every track) elsewhere. When the item we are looking up
204 has a cuesheet document or is a music file with a cuesheet embedded in the tags, and it maps
205 to more than one song then we can not fill the tag data and thumb from the database.
207 MAPSONGS::iterator it
= m_songsMap
.find(pItem
->GetPath()); // Find file in song map
208 if (it
!= m_songsMap
.end() && it
->second
.size() == 1)
210 // Have we loaded this item from database before,
211 // and even if it has a cuesheet it has only one song
212 pItem
->GetMusicInfoTag()->SetSong(it
->second
[0]);
213 if (!it
->second
[0].strThumb
.empty())
214 pItem
->SetArt("thumb", it
->second
[0].strThumb
);
216 else if (pItem
->IsMusicDb())
217 { // a music db item that doesn't have tag loaded - grab details from the database
218 XFILE::MUSICDATABASEDIRECTORY::CQueryParams param
;
219 XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem
->GetPath(),param
);
221 if (m_musicDatabase
.GetSong(param
.GetSongId(), song
))
223 pItem
->GetMusicInfoTag()->SetSong(song
);
224 if (!song
.strThumb
.empty())
225 pItem
->SetArt("thumb", song
.strThumb
);
228 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICFILES_USETAGS
) || pItem
->IsCDDA())
229 { // Nothing found, load tag from file,
230 // always try to load cddb info
231 // get correct tag parser
232 std::unique_ptr
<IMusicInfoTagLoader
> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(*pItem
));
233 if (nullptr != pLoader
)
235 pLoader
->Load(pItem
->GetPath(), *pItem
->GetMusicInfoTag());
239 m_strPrevPath
= strPath
;
246 void CMusicInfoLoader::OnLoaderFinish()
248 // cleanup last loaded songs from database
251 // cleanup cache loaded from HD
252 m_mapFileItems
->Clear();
254 // Save loaded items to HD
255 if (!m_strCacheFileName
.empty())
256 SaveCache(m_strCacheFileName
, *m_pVecItems
);
257 else if (!m_bStop
&& (m_databaseHits
> 1 || m_tagReads
> 0))
260 m_musicDatabase
.Close();
263 m_thumbLoader
->OnLoaderFinish();
266 void CMusicInfoLoader::UseCacheOnHD(const std::string
& strFileName
)
268 m_strCacheFileName
= strFileName
;
271 void CMusicInfoLoader::LoadCache(const std::string
& strFileName
, CFileItemList
& items
)
275 if (file
.Open(strFileName
))
277 CArchive
ar(&file
, CArchive::load
);
280 for (int i
= 0; i
< iSize
; i
++)
282 CFileItemPtr
pItem(new CFileItem());
288 items
.SetFastLookup(true);
292 void CMusicInfoLoader::SaveCache(const std::string
& strFileName
, CFileItemList
& items
)
294 int iSize
= items
.Size();
301 if (file
.OpenForWrite(strFileName
))
303 CArchive
ar(&file
, CArchive::store
);
305 for (int i
= 0; i
< iSize
; i
++)
307 CFileItemPtr pItem
= items
[i
];