summaryrefslogtreecommitdiffstats
path: root/xbmc/music/MusicInfoLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/music/MusicInfoLoader.cpp')
-rw-r--r--xbmc/music/MusicInfoLoader.cpp314
1 files changed, 314 insertions, 0 deletions
diff --git a/xbmc/music/MusicInfoLoader.cpp b/xbmc/music/MusicInfoLoader.cpp
new file mode 100644
index 0000000..2732bd4
--- /dev/null
+++ b/xbmc/music/MusicInfoLoader.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "MusicInfoLoader.h"
+
+#include "Album.h"
+#include "Artist.h"
+#include "FileItem.h"
+#include "MusicDatabase.h"
+#include "MusicThumbLoader.h"
+#include "ServiceBroker.h"
+#include "filesystem/File.h"
+#include "filesystem/MusicDatabaseDirectory/DirectoryNode.h"
+#include "filesystem/MusicDatabaseDirectory/QueryParams.h"
+#include "music/tags/MusicInfoTag.h"
+#include "music/tags/MusicInfoTagLoaderFactory.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/Archive.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+
+using namespace XFILE;
+using namespace MUSIC_INFO;
+
+// HACK until we make this threadable - specify 1 thread only for now
+CMusicInfoLoader::CMusicInfoLoader()
+ : CBackgroundInfoLoader()
+ , m_databaseHits{0}
+ , m_tagReads{0}
+{
+ m_mapFileItems = new CFileItemList;
+
+ m_thumbLoader = new CMusicThumbLoader();
+}
+
+CMusicInfoLoader::~CMusicInfoLoader()
+{
+ StopThread();
+ delete m_mapFileItems;
+ delete m_thumbLoader;
+}
+
+void CMusicInfoLoader::OnLoaderStart()
+{
+ // Load previously cached items from HD
+ if (!m_strCacheFileName.empty())
+ LoadCache(m_strCacheFileName, *m_mapFileItems);
+ else
+ {
+ m_mapFileItems->SetPath(m_pVecItems->GetPath());
+ m_mapFileItems->Load();
+ m_mapFileItems->SetFastLookup(true);
+ }
+
+ m_strPrevPath.clear();
+
+ m_databaseHits = m_tagReads = 0;
+
+ if (m_pProgressCallback)
+ m_pProgressCallback->SetProgressMax(m_pVecItems->GetFileCount());
+
+ m_musicDatabase.Open();
+
+ if (m_thumbLoader)
+ m_thumbLoader->OnLoaderStart();
+}
+
+bool CMusicInfoLoader::LoadAdditionalTagInfo(CFileItem* pItem)
+{
+ if (!pItem || (pItem->m_bIsFolder && !pItem->IsAudio()) ||
+ pItem->IsPlayList() || pItem->IsNFO() || pItem->IsInternetStream())
+ return false;
+
+ if (pItem->GetProperty("hasfullmusictag") == "true")
+ return false; // already have the information
+
+ std::string path(pItem->GetPath());
+ // For songs in library set the (primary) song artist and album properties
+ // Use song Id (not path) as called for items from either library or file view,
+ // but could also be listitem with tag loaded by a script
+ if (pItem->HasMusicInfoTag() &&
+ pItem->GetMusicInfoTag()->GetType() == MediaTypeSong &&
+ pItem->GetMusicInfoTag()->GetDatabaseId() > 0)
+ {
+ CMusicDatabase database;
+ database.Open();
+ // May already have song artist ids as item property set when data read from
+ // db, but check property is valid array (scripts could set item properties
+ // incorrectly), otherwise fetch artist using song id.
+ CArtist artist;
+ bool artistfound = false;
+ if (pItem->HasProperty("artistid") && pItem->GetProperty("artistid").isArray())
+ {
+ CVariant::const_iterator_array varid = pItem->GetProperty("artistid").begin_array();
+ int idArtist = static_cast<int>(varid->asInteger());
+ artistfound = database.GetArtist(idArtist, artist, false);
+ }
+ else
+ artistfound = database.GetArtistFromSong(pItem->GetMusicInfoTag()->GetDatabaseId(), artist);
+ if (artistfound)
+ CMusicDatabase::SetPropertiesFromArtist(*pItem, artist);
+
+ // May already have album id, otherwise fetch album from song id
+ CAlbum album;
+ bool albumfound = false;
+ int idAlbum = pItem->GetMusicInfoTag()->GetAlbumId();
+ if (idAlbum > 0)
+ albumfound = database.GetAlbum(idAlbum, album, false);
+ else
+ albumfound = database.GetAlbumFromSong(pItem->GetMusicInfoTag()->GetDatabaseId(), album);
+ if (albumfound)
+ CMusicDatabase::SetPropertiesFromAlbum(*pItem, album);
+
+ path = pItem->GetMusicInfoTag()->GetURL();
+ }
+
+ CLog::Log(LOGDEBUG, "Loading additional tag info for file {}", path);
+
+ // we load up the actual tag for this file in order to
+ // fetch the lyrics and add it to the current music info tag
+ CFileItem tempItem(path, false);
+ std::unique_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(tempItem));
+ if (nullptr != pLoader)
+ {
+ CMusicInfoTag tag;
+ pLoader->Load(path, tag);
+ pItem->GetMusicInfoTag()->SetLyrics(tag.GetLyrics());
+ pItem->SetProperty("hasfullmusictag", "true");
+ return true;
+ }
+ return false;
+}
+
+bool CMusicInfoLoader::LoadItem(CFileItem* pItem)
+{
+ bool result = LoadItemCached(pItem);
+ result |= LoadItemLookup(pItem);
+
+ return result;
+}
+
+bool CMusicInfoLoader::LoadItemCached(CFileItem* pItem)
+{
+ if ((pItem->m_bIsFolder && !pItem->IsAudio()) ||
+ pItem->IsPlayList() || pItem->IsSmartPlayList() ||
+ StringUtils::StartsWithNoCase(pItem->GetPath(), "newplaylist://") ||
+ StringUtils::StartsWithNoCase(pItem->GetPath(), "newsmartplaylist://") ||
+ pItem->IsNFO() || (pItem->IsInternetStream() && !pItem->IsMusicDb()))
+ return false;
+
+ // Get thumb for item
+ m_thumbLoader->LoadItem(pItem);
+
+ return true;
+}
+
+bool CMusicInfoLoader::LoadItemLookup(CFileItem* pItem)
+{
+ if (m_pProgressCallback && !pItem->m_bIsFolder)
+ m_pProgressCallback->SetProgressAdvance();
+
+ if ((pItem->m_bIsFolder && !pItem->IsAudio()) || //
+ pItem->IsPlayList() || pItem->IsSmartPlayList() || //
+ StringUtils::StartsWithNoCase(pItem->GetPath(), "newplaylist://") || //
+ StringUtils::StartsWithNoCase(pItem->GetPath(), "newsmartplaylist://") || //
+ pItem->IsNFO() || (pItem->IsInternetStream() && !pItem->IsMusicDb()))
+ return false;
+
+ if ((!pItem->HasMusicInfoTag() || !pItem->GetMusicInfoTag()->Loaded()) && pItem->IsAudio())
+ {
+ // first check the cached item
+ CFileItemPtr mapItem = (*m_mapFileItems)[pItem->GetPath()];
+ if (mapItem && mapItem->m_dateTime==pItem->m_dateTime && mapItem->HasMusicInfoTag() && mapItem->GetMusicInfoTag()->Loaded())
+ { // Query map if we previously cached the file on HD
+ *pItem->GetMusicInfoTag() = *mapItem->GetMusicInfoTag();
+ if (mapItem->HasArt("thumb"))
+ pItem->SetArt("thumb", mapItem->GetArt("thumb"));
+ }
+ else
+ {
+ std::string strPath = URIUtils::GetDirectory(pItem->GetPath());
+ URIUtils::AddSlashAtEnd(strPath);
+ if (strPath!=m_strPrevPath)
+ {
+ // The item is from another directory as the last one,
+ // query the database for the new directory...
+ m_musicDatabase.GetSongsByPath(strPath, m_songsMap);
+ m_databaseHits++;
+ }
+
+ /*
+ This only loads the item with the song from the database when it maps to a single song,
+ it can not load song data for items with cuesheets that expand to multiple songs.
+ For songs from embedded or separate cuesheets strFileName is not unique, so the song map for
+ the path will have the list of songs from that file. But items with cuesheets are expanded
+ (replacing each item with items for every track) elsewhere. When the item we are looking up
+ has a cuesheet document or is a music file with a cuesheet embedded in the tags, and it maps
+ to more than one song then we can not fill the tag data and thumb from the database.
+ */
+ MAPSONGS::iterator it = m_songsMap.find(pItem->GetPath()); // Find file in song map
+ if (it != m_songsMap.end() && it->second.size() == 1)
+ {
+ // Have we loaded this item from database before,
+ // and even if it has a cuesheet it has only one song
+ pItem->GetMusicInfoTag()->SetSong(it->second[0]);
+ if (!it->second[0].strThumb.empty())
+ pItem->SetArt("thumb", it->second[0].strThumb);
+ }
+ else if (pItem->IsMusicDb())
+ { // a music db item that doesn't have tag loaded - grab details from the database
+ XFILE::MUSICDATABASEDIRECTORY::CQueryParams param;
+ XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem->GetPath(),param);
+ CSong song;
+ if (m_musicDatabase.GetSong(param.GetSongId(), song))
+ {
+ pItem->GetMusicInfoTag()->SetSong(song);
+ if (!song.strThumb.empty())
+ pItem->SetArt("thumb", song.strThumb);
+ }
+ }
+ else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICFILES_USETAGS) || pItem->IsCDDA())
+ { // Nothing found, load tag from file,
+ // always try to load cddb info
+ // get correct tag parser
+ std::unique_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(*pItem));
+ if (nullptr != pLoader)
+ // get tag
+ pLoader->Load(pItem->GetPath(), *pItem->GetMusicInfoTag());
+ m_tagReads++;
+ }
+
+ m_strPrevPath = strPath;
+ }
+ }
+
+ return true;
+}
+
+void CMusicInfoLoader::OnLoaderFinish()
+{
+ // cleanup last loaded songs from database
+ m_songsMap.clear();
+
+ // cleanup cache loaded from HD
+ m_mapFileItems->Clear();
+
+ // Save loaded items to HD
+ if (!m_strCacheFileName.empty())
+ SaveCache(m_strCacheFileName, *m_pVecItems);
+ else if (!m_bStop && (m_databaseHits > 1 || m_tagReads > 0))
+ m_pVecItems->Save();
+
+ m_musicDatabase.Close();
+
+ if (m_thumbLoader)
+ m_thumbLoader->OnLoaderFinish();
+}
+
+void CMusicInfoLoader::UseCacheOnHD(const std::string& strFileName)
+{
+ m_strCacheFileName = strFileName;
+}
+
+void CMusicInfoLoader::LoadCache(const std::string& strFileName, CFileItemList& items)
+{
+ CFile file;
+
+ if (file.Open(strFileName))
+ {
+ CArchive ar(&file, CArchive::load);
+ int iSize = 0;
+ ar >> iSize;
+ for (int i = 0; i < iSize; i++)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ ar >> *pItem;
+ items.Add(pItem);
+ }
+ ar.Close();
+ file.Close();
+ items.SetFastLookup(true);
+ }
+}
+
+void CMusicInfoLoader::SaveCache(const std::string& strFileName, CFileItemList& items)
+{
+ int iSize = items.Size();
+
+ if (iSize <= 0)
+ return ;
+
+ CFile file;
+
+ if (file.OpenForWrite(strFileName))
+ {
+ CArchive ar(&file, CArchive::store);
+ ar << items.Size();
+ for (int i = 0; i < iSize; i++)
+ {
+ CFileItemPtr pItem = items[i];
+ ar << *pItem;
+ }
+ ar.Close();
+ file.Close();
+ }
+
+}