0ab84c4389ddb3241d5a2016c7ea8502c5cc5eb6
[vuplus_xbmc] / xbmc / utils / EdenVideoArtUpdater.cpp
1 /*
2  *      Copyright (C) 2012-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 "EdenVideoArtUpdater.h"
22 #include "video/VideoDatabase.h"
23 #include "video/VideoInfoScanner.h"
24 #include "FileItem.h"
25 #include "utils/log.h"
26 #include "utils/Crc32.h"
27 #include "utils/URIUtils.h"
28 #include "utils/ScraperUrl.h"
29 #include "utils/StringUtils.h"
30 #include "TextureCache.h"
31 #include "TextureCacheJob.h"
32 #include "pictures/Picture.h"
33 #include "profiles/ProfilesManager.h"
34 #include "settings/AdvancedSettings.h"
35 #include "settings/Settings.h"
36 #include "guilib/Texture.h"
37 #include "guilib/GUIWindowManager.h"
38 #include "guilib/LocalizeStrings.h"
39 #include "filesystem/File.h"
40 #include "filesystem/StackDirectory.h"
41 #include "dialogs/GUIDialogExtendedProgressBar.h"
42 #include "interfaces/AnnouncementManager.h"
43
44 using namespace std;
45 using namespace VIDEO;
46 using namespace XFILE;
47
48 CEdenVideoArtUpdater::CEdenVideoArtUpdater() : CThread("VideoArtUpdater")
49 {
50   m_textureDB.Open();
51 }
52
53 CEdenVideoArtUpdater::~CEdenVideoArtUpdater()
54 {
55   m_textureDB.Close();
56 }
57
58 void CEdenVideoArtUpdater::Start()
59 {
60   CEdenVideoArtUpdater *updater = new CEdenVideoArtUpdater();
61   updater->Create(true); // autodelete
62 }
63
64 void CEdenVideoArtUpdater::Process()
65 {
66   // grab all movies...
67   CVideoDatabase db;
68   if (!db.Open())
69     return;
70
71   CFileItemList items;
72
73   CGUIDialogExtendedProgressBar* dialog =
74     (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
75
76   CGUIDialogProgressBarHandle *handle = dialog->GetHandle(g_localizeStrings.Get(314));
77   handle->SetTitle(g_localizeStrings.Get(12349));
78
79   // movies
80   db.GetMoviesByWhere("videodb://movies/titles/", CDatabase::Filter(), items);
81   for (int i = 0; i < items.Size(); i++)
82   {
83     CFileItemPtr item = items[i];
84     handle->SetProgress(i, items.Size());
85     handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
86     string cachedThumb = GetCachedVideoThumb(*item);
87     string cachedFanart = GetCachedFanart(*item);
88
89     item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
90     item->GetVideoInfoTag()->m_fanart.Unpack();
91     item->GetVideoInfoTag()->m_strPictureURL.Parse();
92
93     map<string, string> artwork;
94     if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
95         || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
96     {
97       CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
98       std::string type;
99       if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
100         artwork.insert(make_pair(type, art));
101
102       art = CVideoInfoScanner::GetFanart(item.get(), true);
103       if (CacheTexture(art, cachedFanart, item->GetLabel()))
104         artwork.insert(make_pair("fanart", art));
105
106       if (artwork.empty())
107         artwork.insert(make_pair("thumb", ""));
108       db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
109     }
110   }
111   items.Clear();
112
113   // music videos
114   db.GetMusicVideosNav("videodb://musicvideos/titles/", items, false);
115   for (int i = 0; i < items.Size(); i++)
116   {
117     CFileItemPtr item = items[i];
118     handle->SetProgress(i, items.Size());
119     handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
120     string cachedThumb = GetCachedVideoThumb(*item);
121     string cachedFanart = GetCachedFanart(*item);
122
123     item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
124     item->GetVideoInfoTag()->m_fanart.Unpack();
125     item->GetVideoInfoTag()->m_strPictureURL.Parse();
126
127     map<string, string> artwork;
128     if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
129         || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
130     {
131       CStdString art = CVideoInfoScanner::GetImage(item.get(), true, item->GetVideoInfoTag()->m_basePath != item->GetPath(), "thumb");
132       std::string type;
133       if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
134         artwork.insert(make_pair(type, art));
135
136       art = CVideoInfoScanner::GetFanart(item.get(), true);
137       if (CacheTexture(art, cachedFanart, item->GetLabel()))
138         artwork.insert(make_pair("fanart", art));
139
140       if (artwork.empty())
141         artwork.insert(make_pair("thumb", ""));
142       db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
143     }
144   }
145   items.Clear();
146
147   // tvshows
148   // count the number of episodes
149   db.GetTvShowsNav("videodb://tvshows/titles/", items);
150   for (int i = 0; i < items.Size(); i++)
151   {
152     CFileItemPtr item = items[i];
153     handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
154     string cachedThumb = GetCachedVideoThumb(*item);
155     string cachedFanart = GetCachedFanart(*item);
156
157     item->SetPath(item->GetVideoInfoTag()->m_strPath);
158     item->GetVideoInfoTag()->m_fanart.Unpack();
159     item->GetVideoInfoTag()->m_strPictureURL.Parse();
160
161     map<string, string> artwork;
162     if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork)
163         || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
164     {
165       CStdString art = CVideoInfoScanner::GetImage(item.get(), true, false, "thumb");
166       std::string type;
167       if (CacheTexture(art, cachedThumb, item->GetLabel(), type))
168         artwork.insert(make_pair(type, art));
169
170       art = CVideoInfoScanner::GetFanart(item.get(), true);
171       if (CacheTexture(art, cachedFanart, item->GetLabel()))
172         artwork.insert(make_pair("fanart", art));
173
174       if (artwork.empty())
175         artwork.insert(make_pair("thumb", ""));
176       db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
177     }
178
179     // now season art...
180     map<int, map<string, string> > seasons;
181     vector<string> artTypes; artTypes.push_back("thumb");
182     CVideoInfoScanner::GetSeasonThumbs(*item->GetVideoInfoTag(), seasons, artTypes, true);
183     for (map<int, map<string, string> >::const_iterator j = seasons.begin(); j != seasons.end(); ++j)
184     {
185       if (j->second.empty())
186         continue;
187       int idSeason = db.AddSeason(item->GetVideoInfoTag()->m_iDbId, j->first);
188       map<string, string> seasonArt;
189       if (idSeason > -1 && !db.GetArtForItem(idSeason, "season", seasonArt))
190       {
191         std::string cachedSeason = GetCachedSeasonThumb(j->first, item->GetVideoInfoTag()->m_strPath);
192         std::string type;
193         std::string originalUrl = j->second.begin()->second;
194         if (CacheTexture(originalUrl, cachedSeason, "", type))
195           db.SetArtForItem(idSeason, "season", type, originalUrl);
196       }
197     }
198
199     // now episodes...
200     CFileItemList items2;
201     db.GetEpisodesByWhere("videodb://tvshows/titles/-1/-1/", db.PrepareSQL("episodeview.idShow=%d", item->GetVideoInfoTag()->m_iDbId), items2);
202     for (int j = 0; j < items2.Size(); j++)
203     {
204       handle->SetProgress(j, items2.Size());
205       CFileItemPtr episode = items2[j];
206       string cachedThumb = GetCachedEpisodeThumb(*episode);
207       if (!CFile::Exists(cachedThumb))
208         cachedThumb = GetCachedVideoThumb(*episode);
209       episode->SetPath(episode->GetVideoInfoTag()->m_strFileNameAndPath);
210       episode->GetVideoInfoTag()->m_strPictureURL.Parse();
211
212       map<string, string> artwork;
213       if (!db.GetArtForItem(episode->GetVideoInfoTag()->m_iDbId, episode->GetVideoInfoTag()->m_type, artwork)
214           || (artwork.size() == 1 && artwork.find("thumb") != artwork.end()))
215       {
216         CStdString art = CVideoInfoScanner::GetImage(episode.get(), true, episode->GetVideoInfoTag()->m_basePath != episode->GetPath(), "thumb");
217         if (CacheTexture(art, cachedThumb, episode->GetLabel()))
218           artwork.insert(make_pair("thumb", art));
219         else
220           artwork.insert(make_pair("thumb", ""));
221         db.SetArtForItem(episode->GetVideoInfoTag()->m_iDbId, episode->GetVideoInfoTag()->m_type, artwork);
222       }
223     }
224   }
225   items.Clear();
226
227   // now sets
228   db.GetSetsNav("videodb://movies/sets/", items, VIDEODB_CONTENT_MOVIES);
229   for (int i = 0; i < items.Size(); i++)
230   {
231     CFileItemPtr item = items[i];
232     handle->SetProgress(i, items.Size());
233     handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
234     map<string, string> artwork;
235     if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
236     { // grab the first movie from this set
237       CFileItemList items2;
238       db.GetMoviesNav("videodb://movies/titles/", items2, -1, -1, -1, -1, -1, -1, item->GetVideoInfoTag()->m_iDbId);
239       if (items2.Size() > 1)
240       {
241         if (db.GetArtForItem(items2[0]->GetVideoInfoTag()->m_iDbId, items2[0]->GetVideoInfoTag()->m_type, artwork))
242           db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
243       }
244     }
245   }
246   items.Clear();
247
248   // now actors
249   if (CSettings::Get().GetBool("videolibrary.actorthumbs"))
250   {
251     db.GetActorsNav("videodb://movies/titles/", items, VIDEODB_CONTENT_MOVIES);
252     db.GetActorsNav("videodb://tvshows/titles/", items, VIDEODB_CONTENT_TVSHOWS);
253     db.GetActorsNav("videodb://tvshows/titles/", items, VIDEODB_CONTENT_EPISODES);
254     db.GetActorsNav("videodb://musicvideos/titles/", items, VIDEODB_CONTENT_MUSICVIDEOS);
255     for (int i = 0; i < items.Size(); i++)
256     {
257       CFileItemPtr item = items[i];
258       handle->SetProgress(i, items.Size());
259       handle->SetText(StringUtils::Format(g_localizeStrings.Get(12350).c_str(), item->GetLabel().c_str()));
260       map<string, string> artwork;
261       if (!db.GetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork))
262       {
263         item->GetVideoInfoTag()->m_strPictureURL.Parse();
264         string cachedThumb = GetCachedActorThumb(*item);
265
266         string art = CScraperUrl::GetThumbURL(item->GetVideoInfoTag()->m_strPictureURL.GetFirstThumb());
267         if (CacheTexture(art, cachedThumb, item->GetLabel()))
268           artwork.insert(make_pair("thumb", art));
269         else
270           artwork.insert(make_pair("thumb", ""));
271         db.SetArtForItem(item->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_type, artwork);
272       }
273     }
274   }
275   handle->MarkFinished();
276
277   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnScanFinished");
278
279   items.Clear();
280 }
281
282 bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label)
283 {
284   std::string type;
285   return CacheTexture(originalUrl, cachedFile, label, type);
286 }
287
288 bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type)
289 {
290   if (!CFile::Exists(cachedFile))
291   {
292     CLog::Log(LOGERROR, "%s No cached art for item %s (should be %s)", __FUNCTION__, label.c_str(), cachedFile.c_str());
293     return false;
294   }
295   if (originalUrl.empty())
296   {
297     originalUrl = GetThumb(cachedFile, "http://unknown/video/", true);
298     CLog::Log(LOGERROR, "%s No original url for item %s, but cached art exists, using %s", __FUNCTION__, label.c_str(), originalUrl.c_str());
299   }
300
301   CTextureDetails details;
302   details.updateable = false;
303   details.hash = "NOHASH";
304   type = "thumb"; // unknown art type
305
306   CBaseTexture *texture = CTextureCacheJob::LoadImage(cachedFile, 0, 0, "");
307   if (texture)
308   {
309     if (texture->HasAlpha())
310       details.file = CTextureCache::GetCacheFile(originalUrl) + ".png";
311     else
312       details.file = CTextureCache::GetCacheFile(originalUrl) + ".jpg";
313
314     CLog::Log(LOGDEBUG, "Caching image '%s' ('%s') to '%s' for item '%s'", originalUrl.c_str(), cachedFile.c_str(), details.file.c_str(), label.c_str());
315
316     uint32_t width = 0, height = 0;
317     if (CPicture::CacheTexture(texture, width, height, CTextureCache::GetCachedPath(details.file)))
318     {
319       details.width = width;
320       details.height = height;
321       type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
322       delete texture;
323       m_textureDB.AddCachedTexture(originalUrl, details);
324       return true;
325     }
326   }
327   CLog::Log(LOGERROR, "Can't cache image '%s' ('%s') for item '%s'", originalUrl.c_str(), cachedFile.c_str(), label.c_str());
328   return false;
329 }
330
331 CStdString CEdenVideoArtUpdater::GetCachedActorThumb(const CFileItem &item)
332 {
333   return GetThumb("actor" + item.GetLabel(), CProfilesManager::Get().GetVideoThumbFolder(), true);
334 }
335
336 CStdString CEdenVideoArtUpdater::GetCachedSeasonThumb(int season, const CStdString &path)
337 {
338   CStdString label;
339   if (season == -1)
340     label = g_localizeStrings.Get(20366);
341   else if (season == 0)
342     label = g_localizeStrings.Get(20381);
343   else
344     label.Format(g_localizeStrings.Get(20358), season);
345   return GetThumb("season" + path + label, CProfilesManager::Get().GetVideoThumbFolder(), true);
346 }
347
348 CStdString CEdenVideoArtUpdater::GetCachedEpisodeThumb(const CFileItem &item)
349 {
350   // get the locally cached thumb
351   CStdString strCRC;
352   strCRC.Format("%sepisode%i", item.GetVideoInfoTag()->m_strFileNameAndPath.c_str(), item.GetVideoInfoTag()->m_iEpisode);
353   return GetThumb(strCRC, CProfilesManager::Get().GetVideoThumbFolder(), true);
354 }
355
356 CStdString CEdenVideoArtUpdater::GetCachedVideoThumb(const CFileItem &item)
357 {
358   if (item.m_bIsFolder && !item.GetVideoInfoTag()->m_strPath.IsEmpty())
359     return GetThumb(item.GetVideoInfoTag()->m_strPath, CProfilesManager::Get().GetVideoThumbFolder(), true);
360   else if (!item.GetVideoInfoTag()->m_strFileNameAndPath.IsEmpty())
361   {
362     CStdString path = item.GetVideoInfoTag()->m_strFileNameAndPath;
363     if (URIUtils::IsStack(path))
364       path = CStackDirectory::GetFirstStackedFile(path);
365     return GetThumb(path, CProfilesManager::Get().GetVideoThumbFolder(), true);
366   }
367   return GetThumb(item.GetPath(), CProfilesManager::Get().GetVideoThumbFolder(), true);
368 }
369
370 CStdString CEdenVideoArtUpdater::GetCachedFanart(const CFileItem &item)
371 {
372   if (!item.GetVideoInfoTag()->m_artist.empty())
373     return GetThumb(StringUtils::Join(item.GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator), URIUtils::AddFileToFolder(CProfilesManager::Get().GetThumbnailsFolder(), "Music/Fanart/"), false);
374   CStdString path = item.GetVideoInfoTag()->GetPath();
375   if (path.empty())
376     return "";
377   return GetThumb(path, URIUtils::AddFileToFolder(CProfilesManager::Get().GetVideoThumbFolder(), "Fanart/"), false);
378 }
379
380 CStdString CEdenVideoArtUpdater::GetThumb(const CStdString &path, const CStdString &path2, bool split)
381 {
382   // get the locally cached thumb
383   Crc32 crc;
384   crc.ComputeFromLowerCase(path);
385
386   CStdString thumb;
387   if (split)
388   {
389     CStdString hex;
390     hex.Format("%08x", (__int32)crc);
391     thumb.Format("%c\\%08x.tbn", hex[0], (unsigned __int32)crc);
392   }
393   else
394     thumb.Format("%08x.tbn", (unsigned __int32)crc);
395
396   return URIUtils::AddFileToFolder(path2, thumb);
397 }