added async resume point retrieving by thumbloader
[xbmc:paulepanters-xbmc.git] / xbmc / ThumbLoader.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "filesystem/StackDirectory.h"
23 #include "ThumbLoader.h"
24 #include "utils/URIUtils.h"
25 #include "URL.h"
26 #include "pictures/Picture.h"
27 #include "filesystem/File.h"
28 #include "FileItem.h"
29 #include "settings/GUISettings.h"
30 #include "GUIUserMessages.h"
31 #include "guilib/GUIWindowManager.h"
32 #include "guilib/TextureManager.h"
33 #include "TextureCache.h"
34 #include "utils/log.h"
35 #include "programs/Shortcut.h"
36 #include "video/VideoInfoTag.h"
37 #include "video/VideoDatabase.h"
38 #include "cores/dvdplayer/DVDFileInfo.h"
39
40 using namespace XFILE;
41 using namespace std;
42
43 CThumbLoader::CThumbLoader(int nThreads) :
44   CBackgroundInfoLoader(nThreads)
45 {
46 }
47
48 CThumbLoader::~CThumbLoader()
49 {
50 }
51
52 bool CThumbLoader::LoadRemoteThumb(CFileItem *pItem)
53 {
54   // look for remote thumbs
55   CStdString thumb(pItem->GetThumbnailImage());
56   if (!g_TextureManager.CanLoad(thumb))
57   {
58     CStdString cachedThumb(pItem->GetCachedVideoThumb());
59     if (CFile::Exists(cachedThumb))
60       pItem->SetThumbnailImage(cachedThumb);
61     else
62     {
63       if (CPicture::CreateThumbnail(thumb, cachedThumb))
64         pItem->SetThumbnailImage(cachedThumb);
65       else
66         pItem->SetThumbnailImage("");
67     }
68   }
69   return pItem->HasThumbnail();
70 }
71
72 CStdString CThumbLoader::GetCachedThumb(const CFileItem &item)
73 {
74   CTextureDatabase db;
75   if (db.Open())
76     return db.GetTextureForPath(item.GetPath());
77   return "";
78 }
79
80 bool CThumbLoader::CheckAndCacheThumb(CFileItem &item)
81 {
82   if (item.HasThumbnail() && !g_TextureManager.CanLoad(item.GetThumbnailImage()))
83   {
84     CStdString thumb = CTextureCache::Get().CheckAndCacheImage(item.GetThumbnailImage());
85     item.SetThumbnailImage(thumb);
86     return !thumb.IsEmpty();
87   }
88   return false;
89 }
90
91 CThumbExtractor::CThumbExtractor(const CFileItem& item, const CStdString& listpath, bool thumb, const CStdString& target)
92 {
93   m_listpath = listpath;
94   m_target = target;
95   m_thumb = thumb;
96   m_item = item;
97
98   m_path = item.GetPath();
99
100   if (item.IsVideoDb() && item.HasVideoInfoTag())
101     m_path = item.GetVideoInfoTag()->m_strFileNameAndPath;
102
103   if (URIUtils::IsStack(m_path))
104     m_path = CStackDirectory::GetFirstStackedFile(m_path);
105 }
106
107 CThumbExtractor::~CThumbExtractor()
108 {
109 }
110
111 bool CThumbExtractor::operator==(const CJob* job) const
112 {
113   if (strcmp(job->GetType(),GetType()) == 0)
114   {
115     const CThumbExtractor* jobExtract = dynamic_cast<const CThumbExtractor*>(job);
116     if (jobExtract && jobExtract->m_listpath == m_listpath)
117       return true;
118   }
119   return false;
120 }
121
122 bool CThumbExtractor::DoWork()
123 {
124   if (URIUtils::IsLiveTV(m_path)
125   ||  URIUtils::IsUPnP(m_path)
126   ||  URIUtils::IsDAAP(m_path)
127   ||  m_item.IsDVD()
128   ||  m_item.IsDVDImage()
129   ||  m_item.IsDVDFile(false, true)
130   ||  m_item.IsInternetStream()
131   ||  m_item.IsPlayList())
132     return false;
133
134   if (URIUtils::IsRemote(m_path) && !URIUtils::IsOnLAN(m_path))
135     return false;
136
137   bool result=false;
138   if (m_thumb)
139   {
140     CLog::Log(LOGDEBUG,"%s - trying to extract thumb from video file %s", __FUNCTION__, m_path.c_str());
141     result = CDVDFileInfo::ExtractThumb(m_path, m_target, &m_item.GetVideoInfoTag()->m_streamDetails);
142     if(result)
143     {
144       m_item.SetProperty("HasAutoThumb", "1");
145       m_item.SetProperty("AutoThumbImage", m_target);
146       m_item.SetThumbnailImage(m_target);
147     }
148   }
149   else if (m_item.HasVideoInfoTag() && !m_item.GetVideoInfoTag()->HasStreamDetails())
150   {
151     CLog::Log(LOGDEBUG,"%s - trying to extract filestream details from video file %s", __FUNCTION__, m_path.c_str());
152     result = CDVDFileInfo::GetFileStreamDetails(&m_item);
153   }
154
155   return result;
156 }
157
158 CVideoThumbLoader::CVideoThumbLoader() :
159   CThumbLoader(1), CJobQueue(true), m_pStreamDetailsObs(NULL)
160 {
161 }
162
163 CVideoThumbLoader::~CVideoThumbLoader()
164 {
165   StopThread();
166 }
167
168 void CVideoThumbLoader::OnLoaderStart()
169 {
170 }
171
172 void CVideoThumbLoader::OnLoaderFinish()
173 {
174 }
175
176 /**
177  * Look for a thumbnail for pItem.  If one does not exist, look for an autogenerated
178  * thumbnail.  If that does not exist, attempt to autogenerate one.  Finally, check
179  * for the existance of fanart and set properties accordinly.
180  * @return: true if pItem has been modified
181  */
182 bool CVideoThumbLoader::LoadItem(CFileItem* pItem)
183 {
184   if (pItem->m_bIsShareOrDrive
185   ||  pItem->IsParentFolder())
186     return false;
187
188   CFileItem item(*pItem);
189   CStdString cachedThumb(item.GetCachedVideoThumb());
190
191   if (!pItem->HasThumbnail())
192   {
193     item.SetUserVideoThumb();
194     if (CFile::Exists(cachedThumb))
195       pItem->SetThumbnailImage(cachedThumb);
196     else
197     {
198       CStdString strPath, strFileName;
199       URIUtils::Split(cachedThumb, strPath, strFileName);
200
201       // create unique thumb for auto generated thumbs
202       cachedThumb = strPath + "auto-" + strFileName;
203       if (CFile::Exists(cachedThumb))
204       {
205         // this is abit of a hack to avoid loading zero sized images
206         // which we know will fail. They will just display empty image
207         // we should really have some way for the texture loader to
208         // do fallbacks to default images for a failed image instead
209         struct __stat64 st;
210         if(CFile::Stat(cachedThumb, &st) == 0 && st.st_size > 0)
211         {
212           pItem->SetProperty("HasAutoThumb", "1");
213           pItem->SetProperty("AutoThumbImage", cachedThumb);
214           pItem->SetThumbnailImage(cachedThumb);
215         }
216       }
217       else if (!item.m_bIsFolder && item.IsVideo() && g_guiSettings.GetBool("myvideos.extractthumb") &&
218                g_guiSettings.GetBool("myvideos.extractflags"))
219       {
220         CThumbExtractor* extract = new CThumbExtractor(item, pItem->GetPath(), true, cachedThumb);
221         AddJob(extract);
222       }
223     }
224   }
225   else if (!pItem->GetThumbnailImage().Left(10).Equals("special://"))
226     LoadRemoteThumb(pItem);
227
228   if (!pItem->HasProperty("fanart_image"))
229   {
230     if (pItem->CacheLocalFanart())
231       pItem->SetProperty("fanart_image",pItem->GetCachedFanart());
232   }
233
234   if (!pItem->m_bIsFolder &&
235        pItem->HasVideoInfoTag() &&
236        g_guiSettings.GetBool("myvideos.extractflags") &&
237        (!pItem->GetVideoInfoTag()->HasStreamDetails() ||
238          pItem->GetVideoInfoTag()->m_streamDetails.GetVideoDuration() <= 0))
239   {
240     CThumbExtractor* extract = new CThumbExtractor(*pItem,pItem->GetPath(),false);
241     AddJob(extract);
242   }
243
244   if (pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds == 0)
245   {
246     CVideoDatabase db;
247     db.Open();
248     db.GetResumePoint(*pItem->GetVideoInfoTag());
249     db.Close();
250   }
251   return true;
252 }
253
254 void CVideoThumbLoader::OnJobComplete(unsigned int jobID, bool success, CJob* job)
255 {
256   if (success)
257   {
258     CThumbExtractor* loader = (CThumbExtractor*)job;
259     loader->m_item.SetPath(loader->m_listpath);
260     CVideoInfoTag* info = loader->m_item.GetVideoInfoTag();
261     if (m_pStreamDetailsObs)
262       m_pStreamDetailsObs->OnStreamDetails(info->m_streamDetails, info->m_strFileNameAndPath, info->m_iFileId);
263     if (m_pObserver)
264       m_pObserver->OnItemLoaded(&loader->m_item);
265     CFileItemPtr pItem(new CFileItem(loader->m_item));
266     CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, pItem);
267     g_windowManager.SendThreadMessage(msg);
268   }
269   CJobQueue::OnJobComplete(jobID, success, job);
270 }
271
272 CProgramThumbLoader::CProgramThumbLoader()
273 {
274 }
275
276 CProgramThumbLoader::~CProgramThumbLoader()
277 {
278 }
279
280 bool CProgramThumbLoader::LoadItem(CFileItem *pItem)
281 {
282   if (pItem->IsParentFolder()) return true;
283   return FillThumb(*pItem);
284 }
285
286 bool CProgramThumbLoader::FillThumb(CFileItem &item)
287 {
288   // no need to do anything if we already have a thumb set
289   if (CheckAndCacheThumb(item) || item.HasThumbnail())
290     return true;
291
292   // see whether we have a cached image for this item
293   CStdString thumb = GetCachedThumb(item);
294   if (!thumb.IsEmpty())
295   {
296     item.SetThumbnailImage(CTextureCache::Get().CheckAndCacheImage(thumb));
297     return true;
298   }
299   thumb = GetLocalThumb(item);
300   if (!thumb.IsEmpty())
301   {
302     CTextureDatabase db;
303     if (db.Open())
304       db.SetTextureForPath(item.GetPath(), thumb);
305     thumb = CTextureCache::Get().CheckAndCacheImage(thumb);
306   }
307   item.SetThumbnailImage(thumb);
308   return true;
309 }
310
311 CStdString CProgramThumbLoader::GetLocalThumb(const CFileItem &item)
312 {
313   // look for the thumb
314   if (item.IsShortCut())
315   {
316     CShortcut shortcut;
317     if ( shortcut.Create( item.GetPath() ) )
318     {
319       // use the shortcut's thumb
320       if (!shortcut.m_strThumb.IsEmpty())
321         return shortcut.m_strThumb;
322       else
323       {
324         CFileItem cut(shortcut.m_strPath,false);
325         if (FillThumb(cut))
326           return cut.GetThumbnailImage();
327       }
328     }
329   }
330   else if (item.m_bIsFolder)
331   {
332     CStdString folderThumb = item.GetFolderThumb();
333     if (XFILE::CFile::Exists(folderThumb))
334       return folderThumb;
335   }
336   else
337   {
338     CStdString fileThumb(item.GetTBNFile());
339     if (CFile::Exists(fileThumb))
340       return fileThumb;
341   }
342   return "";
343 }
344
345 CMusicThumbLoader::CMusicThumbLoader()
346 {
347 }
348
349 CMusicThumbLoader::~CMusicThumbLoader()
350 {
351 }
352
353 bool CMusicThumbLoader::LoadItem(CFileItem* pItem)
354 {
355   if (pItem->m_bIsShareOrDrive) return true;
356   if (!pItem->HasThumbnail())
357     pItem->SetUserMusicThumb();
358   else
359     LoadRemoteThumb(pItem);
360   return true;
361 }
362