[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / music / MusicInfoLoader.cpp
blob2732bd4d3fbfdeb0a5227ee44d3ea8c3d2b1bb63
1 /*
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.
7 */
9 #include "MusicInfoLoader.h"
11 #include "Album.h"
12 #include "Artist.h"
13 #include "FileItem.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()
35 , m_databaseHits{0}
36 , m_tagReads{0}
38 m_mapFileItems = new CFileItemList;
40 m_thumbLoader = new CMusicThumbLoader();
43 CMusicInfoLoader::~CMusicInfoLoader()
45 StopThread();
46 delete m_mapFileItems;
47 delete m_thumbLoader;
50 void CMusicInfoLoader::OnLoaderStart()
52 // Load previously cached items from HD
53 if (!m_strCacheFileName.empty())
54 LoadCache(m_strCacheFileName, *m_mapFileItems);
55 else
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();
71 if (m_thumbLoader)
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())
79 return false;
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;
93 database.Open();
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.
97 CArtist artist;
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);
105 else
106 artistfound = database.GetArtistFromSong(pItem->GetMusicInfoTag()->GetDatabaseId(), artist);
107 if (artistfound)
108 CMusicDatabase::SetPropertiesFromArtist(*pItem, artist);
110 // May already have album id, otherwise fetch album from song id
111 CAlbum album;
112 bool albumfound = false;
113 int idAlbum = pItem->GetMusicInfoTag()->GetAlbumId();
114 if (idAlbum > 0)
115 albumfound = database.GetAlbum(idAlbum, album, false);
116 else
117 albumfound = database.GetAlbumFromSong(pItem->GetMusicInfoTag()->GetDatabaseId(), album);
118 if (albumfound)
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)
132 CMusicInfoTag tag;
133 pLoader->Load(path, tag);
134 pItem->GetMusicInfoTag()->SetLyrics(tag.GetLyrics());
135 pItem->SetProperty("hasfullmusictag", "true");
136 return true;
138 return false;
141 bool CMusicInfoLoader::LoadItem(CFileItem* pItem)
143 bool result = LoadItemCached(pItem);
144 result |= LoadItemLookup(pItem);
146 return result;
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()))
156 return false;
158 // Get thumb for item
159 m_thumbLoader->LoadItem(pItem);
161 return true;
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()))
174 return false;
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"));
186 else
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);
195 m_databaseHits++;
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);
220 CSong song;
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)
234 // get tag
235 pLoader->Load(pItem->GetPath(), *pItem->GetMusicInfoTag());
236 m_tagReads++;
239 m_strPrevPath = strPath;
243 return true;
246 void CMusicInfoLoader::OnLoaderFinish()
248 // cleanup last loaded songs from database
249 m_songsMap.clear();
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))
258 m_pVecItems->Save();
260 m_musicDatabase.Close();
262 if (m_thumbLoader)
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)
273 CFile file;
275 if (file.Open(strFileName))
277 CArchive ar(&file, CArchive::load);
278 int iSize = 0;
279 ar >> iSize;
280 for (int i = 0; i < iSize; i++)
282 CFileItemPtr pItem(new CFileItem());
283 ar >> *pItem;
284 items.Add(pItem);
286 ar.Close();
287 file.Close();
288 items.SetFastLookup(true);
292 void CMusicInfoLoader::SaveCache(const std::string& strFileName, CFileItemList& items)
294 int iSize = items.Size();
296 if (iSize <= 0)
297 return ;
299 CFile file;
301 if (file.OpenForWrite(strFileName))
303 CArchive ar(&file, CArchive::store);
304 ar << items.Size();
305 for (int i = 0; i < iSize; i++)
307 CFileItemPtr pItem = items[i];
308 ar << *pItem;
310 ar.Close();
311 file.Close();