Merge pull request #4857 from t-nelson/Gotham_13.2_backports
[vuplus_xbmc] / xbmc / music / MusicInfoLoader.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "MusicInfoLoader.h"
22 #include "MusicDatabase.h"
23 #include "music/infoscanner/MusicInfoScanner.h"
24 #include "music/tags/MusicInfoTagLoaderFactory.h"
25 #include "filesystem/DirectoryCache.h"
26 #include "filesystem/MusicDatabaseDirectory.h"
27 #include "filesystem/MusicDatabaseDirectory/DirectoryNode.h"
28 #include "filesystem/MusicDatabaseDirectory/QueryParams.h"
29 #include "utils/URIUtils.h"
30 #include "music/tags/MusicInfoTag.h"
31 #include "filesystem/File.h"
32 #include "settings/Settings.h"
33 #include "FileItem.h"
34 #include "utils/log.h"
35 #include "Artist.h"
36 #include "Album.h"
37 #include "MusicThumbLoader.h"
38
39 using namespace std;
40 using namespace XFILE;
41 using namespace MUSIC_INFO;
42
43 // HACK until we make this threadable - specify 1 thread only for now
44 CMusicInfoLoader::CMusicInfoLoader() : CBackgroundInfoLoader()
45 {
46   m_mapFileItems = new CFileItemList;
47
48   m_thumbLoader = new CMusicThumbLoader();
49 }
50
51 CMusicInfoLoader::~CMusicInfoLoader()
52 {
53   StopThread();
54   delete m_mapFileItems;
55   delete m_thumbLoader;
56 }
57
58 void CMusicInfoLoader::OnLoaderStart()
59 {
60   // Load previously cached items from HD
61   if (!m_strCacheFileName.empty())
62     LoadCache(m_strCacheFileName, *m_mapFileItems);
63   else
64   {
65     m_mapFileItems->SetPath(m_pVecItems->GetPath());
66     m_mapFileItems->Load();
67     m_mapFileItems->SetFastLookup(true);
68   }
69
70   m_strPrevPath.clear();
71
72   m_databaseHits = m_tagReads = 0;
73
74   if (m_pProgressCallback)
75     m_pProgressCallback->SetProgressMax(m_pVecItems->GetFileCount());
76
77   m_musicDatabase.Open();
78
79   if (m_thumbLoader)
80     m_thumbLoader->OnLoaderStart();
81 }
82
83 bool CMusicInfoLoader::LoadAdditionalTagInfo(CFileItem* pItem)
84 {
85   if (!pItem || pItem->m_bIsFolder || pItem->IsPlayList() || pItem->IsNFO() || pItem->IsInternetStream())
86     return false;
87
88   if (pItem->GetProperty("hasfullmusictag") == "true")
89     return false; // already have the information
90
91   CStdString path(pItem->GetPath());
92   if (pItem->IsMusicDb())
93   {
94     // set the artist / album properties
95     XFILE::MUSICDATABASEDIRECTORY::CQueryParams param;
96     XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem->GetPath(),param);
97     CArtist artist;
98     CMusicDatabase database;
99     database.Open();
100     if (database.GetArtist(param.GetArtistId(), artist, false))
101       CMusicDatabase::SetPropertiesFromArtist(*pItem,artist);
102
103     CAlbum album;
104     if (database.GetAlbum(param.GetAlbumId(), album, false))
105       CMusicDatabase::SetPropertiesFromAlbum(*pItem,album);
106
107     path = pItem->GetMusicInfoTag()->GetURL();
108   }
109
110   CLog::Log(LOGDEBUG, "Loading additional tag info for file %s", path.c_str());
111
112   // we load up the actual tag for this file
113   auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(path));
114   if (NULL != pLoader.get())
115   {
116     CMusicInfoTag tag;
117     pLoader->Load(path, tag);
118     // then we set the fields from the file tags to the item
119     pItem->SetProperty("lyrics", tag.GetLyrics());
120     pItem->SetProperty("hasfullmusictag", "true");
121     return true;
122   }
123   return false;
124 }
125
126 bool CMusicInfoLoader::LoadItem(CFileItem* pItem)
127 {
128   bool result  = LoadItemCached(pItem);
129        result |= LoadItemLookup(pItem);
130
131   return result;
132 }
133
134 bool CMusicInfoLoader::LoadItemCached(CFileItem* pItem)
135 {
136   if (pItem->m_bIsFolder || pItem->IsPlayList() || pItem->IsNFO() || pItem->IsInternetStream())
137     return false;
138
139   // Get thumb for item
140   m_thumbLoader->LoadItem(pItem);
141
142   return true;
143 }
144
145 bool CMusicInfoLoader::LoadItemLookup(CFileItem* pItem)
146 {
147   if (m_pProgressCallback && !pItem->m_bIsFolder)
148     m_pProgressCallback->SetProgressAdvance();
149
150   if (pItem->m_bIsFolder || pItem->IsPlayList() || pItem->IsNFO() || pItem->IsInternetStream())
151     return false;
152
153   if (!pItem->HasMusicInfoTag() || !pItem->GetMusicInfoTag()->Loaded())
154   {
155     // first check the cached item
156     CFileItemPtr mapItem = (*m_mapFileItems)[pItem->GetPath()];
157     if (mapItem && mapItem->m_dateTime==pItem->m_dateTime && mapItem->HasMusicInfoTag() && mapItem->GetMusicInfoTag()->Loaded())
158     { // Query map if we previously cached the file on HD
159       *pItem->GetMusicInfoTag() = *mapItem->GetMusicInfoTag();
160       if (mapItem->HasArt("thumb"))
161         pItem->SetArt("thumb", mapItem->GetArt("thumb"));
162     }
163     else
164     {
165       CStdString strPath = URIUtils::GetDirectory(pItem->GetPath());
166       URIUtils::AddSlashAtEnd(strPath);
167       if (strPath!=m_strPrevPath)
168       {
169         // The item is from another directory as the last one,
170         // query the database for the new directory...
171         m_musicDatabase.GetSongsByPath(strPath, m_songsMap);
172         m_databaseHits++;
173       }
174
175       MAPSONGS::iterator it = m_songsMap.find(pItem->GetPath());
176       if (it != m_songsMap.end())
177       {  // Have we loaded this item from database before
178         pItem->GetMusicInfoTag()->SetSong(it->second);
179         if (!it->second.strThumb.empty())
180           pItem->SetArt("thumb", it->second.strThumb);
181       }
182       else if (pItem->IsMusicDb())
183       { // a music db item that doesn't have tag loaded - grab details from the database
184         XFILE::MUSICDATABASEDIRECTORY::CQueryParams param;
185         XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem->GetPath(),param);
186         CSong song;
187         if (m_musicDatabase.GetSong(param.GetSongId(), song))
188         {
189           pItem->GetMusicInfoTag()->SetSong(song);
190           if (!song.strThumb.empty())
191             pItem->SetArt("thumb", song.strThumb);
192         }
193       }
194       else if (CSettings::Get().GetBool("musicfiles.usetags") || pItem->IsCDDA())
195       { // Nothing found, load tag from file,
196         // always try to load cddb info
197         // get correct tag parser
198         auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(pItem->GetPath()));
199         if (NULL != pLoader.get())
200           // get tag
201           pLoader->Load(pItem->GetPath(), *pItem->GetMusicInfoTag());
202         m_tagReads++;
203       }
204
205       m_strPrevPath = strPath;
206     }
207   }
208
209   return true;
210 }
211
212 void CMusicInfoLoader::OnLoaderFinish()
213 {
214   // cleanup last loaded songs from database
215   m_songsMap.clear();
216
217   // cleanup cache loaded from HD
218   m_mapFileItems->Clear();
219
220   // Save loaded items to HD
221   if (!m_strCacheFileName.empty())
222     SaveCache(m_strCacheFileName, *m_pVecItems);
223   else if (!m_bStop && (m_databaseHits > 1 || m_tagReads > 0))
224     m_pVecItems->Save();
225
226   m_musicDatabase.Close();
227
228   if (m_thumbLoader)
229     m_thumbLoader->OnLoaderFinish();
230 }
231
232 void CMusicInfoLoader::UseCacheOnHD(const CStdString& strFileName)
233 {
234   m_strCacheFileName = strFileName;
235 }
236
237 void CMusicInfoLoader::LoadCache(const CStdString& strFileName, CFileItemList& items)
238 {
239   CFile file;
240
241   if (file.Open(strFileName))
242   {
243     CArchive ar(&file, CArchive::load);
244     int iSize = 0;
245     ar >> iSize;
246     for (int i = 0; i < iSize; i++)
247     {
248       CFileItemPtr pItem(new CFileItem());
249       ar >> *pItem;
250       items.Add(pItem);
251     }
252     ar.Close();
253     file.Close();
254     items.SetFastLookup(true);
255   }
256 }
257
258 void CMusicInfoLoader::SaveCache(const CStdString& strFileName, CFileItemList& items)
259 {
260   int iSize = items.Size();
261
262   if (iSize <= 0)
263     return ;
264
265   CFile file;
266
267   if (file.OpenForWrite(strFileName))
268   {
269     CArchive ar(&file, CArchive::store);
270     ar << (int)items.Size();
271     for (int i = 0; i < iSize; i++)
272     {
273       CFileItemPtr pItem = items[i];
274       ar << *pItem;
275     }
276     ar.Close();
277     file.Close();
278   }
279
280 }