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 "FileItemList.h"
15 #include "MusicDatabase.h"
16 #include "MusicThumbLoader.h"
17 #include "ServiceBroker.h"
18 #include "filesystem/File.h"
19 #include "filesystem/MusicDatabaseDirectory/DirectoryNode.h"
20 #include "filesystem/MusicDatabaseDirectory/QueryParams.h"
21 #include "music/MusicFileItemClassify.h"
22 #include "music/tags/MusicInfoTag.h"
23 #include "music/tags/MusicInfoTagLoaderFactory.h"
24 #include "network/NetworkFileItemClassify.h"
25 #include "settings/Settings.h"
26 #include "settings/SettingsComponent.h"
27 #include "utils/Archive.h"
28 #include "utils/StringUtils.h"
29 #include "utils/URIUtils.h"
30 #include "utils/log.h"
33 using namespace MUSIC_INFO
;
34 using namespace XFILE
;
36 // HACK until we make this threadable - specify 1 thread only for now
37 CMusicInfoLoader::CMusicInfoLoader() : CBackgroundInfoLoader()
39 m_mapFileItems
= new CFileItemList
;
41 m_thumbLoader
= new CMusicThumbLoader();
44 CMusicInfoLoader::~CMusicInfoLoader()
47 delete m_mapFileItems
;
51 void CMusicInfoLoader::OnLoaderStart()
53 // Load previously cached items from HD
54 if (!m_strCacheFileName
.empty())
55 LoadCache(m_strCacheFileName
, *m_mapFileItems
);
58 m_mapFileItems
->SetPath(m_pVecItems
->GetPath());
59 m_mapFileItems
->Load();
60 m_mapFileItems
->SetFastLookup(true);
63 m_strPrevPath
.clear();
65 m_databaseHits
= m_tagReads
= 0;
67 if (m_pProgressCallback
)
68 m_pProgressCallback
->SetProgressMax(m_pVecItems
->GetFileCount());
70 m_musicDatabase
.Open();
73 m_thumbLoader
->OnLoaderStart();
76 bool CMusicInfoLoader::LoadAdditionalTagInfo(CFileItem
* pItem
)
78 if (!pItem
|| (pItem
->m_bIsFolder
&& !MUSIC::IsAudio(*pItem
)) || pItem
->IsPlayList() ||
79 pItem
->IsNFO() || NETWORK::IsInternetStream(*pItem
))
82 if (pItem
->GetProperty("hasfullmusictag") == "true")
83 return false; // already have the information
85 std::string
path(pItem
->GetPath());
86 // For songs in library set the (primary) song artist and album properties
87 // Use song Id (not path) as called for items from either library or file view,
88 // but could also be listitem with tag loaded by a script
89 if (pItem
->HasMusicInfoTag() &&
90 pItem
->GetMusicInfoTag()->GetType() == MediaTypeSong
&&
91 pItem
->GetMusicInfoTag()->GetDatabaseId() > 0)
93 CMusicDatabase database
;
95 // May already have song artist ids as item property set when data read from
96 // db, but check property is valid array (scripts could set item properties
97 // incorrectly), otherwise fetch artist using song id.
99 bool artistfound
= false;
100 if (pItem
->HasProperty("artistid") && pItem
->GetProperty("artistid").isArray())
102 CVariant::const_iterator_array varid
= pItem
->GetProperty("artistid").begin_array();
103 int idArtist
= static_cast<int>(varid
->asInteger());
104 artistfound
= database
.GetArtist(idArtist
, artist
, false);
107 artistfound
= database
.GetArtistFromSong(pItem
->GetMusicInfoTag()->GetDatabaseId(), artist
);
109 CMusicDatabase::SetPropertiesFromArtist(*pItem
, artist
);
111 // May already have album id, otherwise fetch album from song id
113 bool albumfound
= false;
114 int idAlbum
= pItem
->GetMusicInfoTag()->GetAlbumId();
116 albumfound
= database
.GetAlbum(idAlbum
, album
, false);
118 albumfound
= database
.GetAlbumFromSong(pItem
->GetMusicInfoTag()->GetDatabaseId(), album
);
120 CMusicDatabase::SetPropertiesFromAlbum(*pItem
, album
);
122 path
= pItem
->GetMusicInfoTag()->GetURL();
125 CLog::Log(LOGDEBUG
, "Loading additional tag info for file {}", path
);
127 // we load up the actual tag for this file in order to
128 // fetch the lyrics and add it to the current music info tag
129 CFileItem
tempItem(path
, false);
130 std::unique_ptr
<IMusicInfoTagLoader
> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(tempItem
));
131 if (nullptr != pLoader
)
134 pLoader
->Load(path
, tag
);
135 pItem
->GetMusicInfoTag()->SetLyrics(tag
.GetLyrics());
136 pItem
->SetProperty("hasfullmusictag", "true");
142 bool CMusicInfoLoader::LoadItem(CFileItem
* pItem
)
144 bool result
= LoadItemCached(pItem
);
145 result
|= LoadItemLookup(pItem
);
150 bool CMusicInfoLoader::LoadItemCached(CFileItem
* pItem
)
152 if ((pItem
->m_bIsFolder
&& !MUSIC::IsAudio(*pItem
)) || pItem
->IsPlayList() ||
153 pItem
->IsSmartPlayList() ||
154 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newplaylist://") ||
155 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newsmartplaylist://") || pItem
->IsNFO() ||
156 (NETWORK::IsInternetStream(*pItem
) && !MUSIC::IsMusicDb(*pItem
)))
159 // Get thumb for item
160 m_thumbLoader
->LoadItem(pItem
);
165 bool CMusicInfoLoader::LoadItemLookup(CFileItem
* pItem
)
167 if (m_pProgressCallback
&& !pItem
->m_bIsFolder
)
168 m_pProgressCallback
->SetProgressAdvance();
170 if ((pItem
->m_bIsFolder
&& !MUSIC::IsAudio(*pItem
)) || //
171 pItem
->IsPlayList() || pItem
->IsSmartPlayList() || //
172 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newplaylist://") || //
173 StringUtils::StartsWithNoCase(pItem
->GetPath(), "newsmartplaylist://") || //
174 pItem
->IsNFO() || (NETWORK::IsInternetStream(*pItem
) && !MUSIC::IsMusicDb(*pItem
)))
177 if ((!pItem
->HasMusicInfoTag() || !pItem
->GetMusicInfoTag()->Loaded()) && MUSIC::IsAudio(*pItem
))
179 // first check the cached item
180 CFileItemPtr mapItem
= (*m_mapFileItems
)[pItem
->GetPath()];
181 if (mapItem
&& mapItem
->m_dateTime
==pItem
->m_dateTime
&& mapItem
->HasMusicInfoTag() && mapItem
->GetMusicInfoTag()->Loaded())
182 { // Query map if we previously cached the file on HD
183 *pItem
->GetMusicInfoTag() = *mapItem
->GetMusicInfoTag();
184 if (mapItem
->HasArt("thumb"))
185 pItem
->SetArt("thumb", mapItem
->GetArt("thumb"));
189 std::string strPath
= URIUtils::GetDirectory(pItem
->GetPath());
190 URIUtils::AddSlashAtEnd(strPath
);
191 if (strPath
!=m_strPrevPath
)
193 // The item is from another directory as the last one,
194 // query the database for the new directory...
195 m_musicDatabase
.GetSongsByPath(strPath
, m_songsMap
);
200 This only loads the item with the song from the database when it maps to a single song,
201 it can not load song data for items with cuesheets that expand to multiple songs.
202 For songs from embedded or separate cuesheets strFileName is not unique, so the song map for
203 the path will have the list of songs from that file. But items with cuesheets are expanded
204 (replacing each item with items for every track) elsewhere. When the item we are looking up
205 has a cuesheet document or is a music file with a cuesheet embedded in the tags, and it maps
206 to more than one song then we can not fill the tag data and thumb from the database.
208 MAPSONGS::iterator it
= m_songsMap
.find(pItem
->GetPath()); // Find file in song map
209 if (it
!= m_songsMap
.end() && it
->second
.size() == 1)
211 // Have we loaded this item from database before,
212 // and even if it has a cuesheet it has only one song
213 pItem
->GetMusicInfoTag()->SetSong(it
->second
[0]);
214 if (!it
->second
[0].strThumb
.empty())
215 pItem
->SetArt("thumb", it
->second
[0].strThumb
);
217 else if (MUSIC::IsMusicDb(*pItem
))
218 { // a music db item that doesn't have tag loaded - grab details from the database
219 XFILE::MUSICDATABASEDIRECTORY::CQueryParams param
;
220 XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem
->GetPath(),param
);
222 if (m_musicDatabase
.GetSong(param
.GetSongId(), song
))
224 pItem
->GetMusicInfoTag()->SetSong(song
);
225 if (!song
.strThumb
.empty())
226 pItem
->SetArt("thumb", song
.strThumb
);
229 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
230 CSettings::SETTING_MUSICFILES_USETAGS
) ||
231 MUSIC::IsCDDA(*pItem
))
232 { // Nothing found, load tag from file,
233 // always try to load cddb info
234 // get correct tag parser
235 std::unique_ptr
<IMusicInfoTagLoader
> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(*pItem
));
236 if (nullptr != pLoader
)
238 pLoader
->Load(pItem
->GetPath(), *pItem
->GetMusicInfoTag());
242 m_strPrevPath
= strPath
;
249 void CMusicInfoLoader::OnLoaderFinish()
251 // cleanup last loaded songs from database
254 // cleanup cache loaded from HD
255 m_mapFileItems
->Clear();
257 // Save loaded items to HD
258 if (!m_strCacheFileName
.empty())
259 SaveCache(m_strCacheFileName
, *m_pVecItems
);
260 else if (!m_bStop
&& (m_databaseHits
> 1 || m_tagReads
> 0))
263 m_musicDatabase
.Close();
266 m_thumbLoader
->OnLoaderFinish();
269 void CMusicInfoLoader::UseCacheOnHD(const std::string
& strFileName
)
271 m_strCacheFileName
= strFileName
;
274 void CMusicInfoLoader::LoadCache(const std::string
& strFileName
, CFileItemList
& items
)
278 if (file
.Open(strFileName
))
280 CArchive
ar(&file
, CArchive::load
);
283 for (int i
= 0; i
< iSize
; i
++)
285 CFileItemPtr
pItem(new CFileItem());
291 items
.SetFastLookup(true);
295 void CMusicInfoLoader::SaveCache(const std::string
& strFileName
, CFileItemList
& items
)
297 int iSize
= items
.Size();
304 if (file
.OpenForWrite(strFileName
))
306 CArchive
ar(&file
, CArchive::store
);
308 for (int i
= 0; i
< iSize
; i
++)
310 CFileItemPtr pItem
= items
[i
];