299a2c242e31812d61846a8c668cd813b8af1327
[vuplus_xbmc] / xbmc / TextureDatabase.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 "TextureDatabase.h"
22 #include "utils/log.h"
23 #include "XBDateTime.h"
24 #include "dbwrappers/dataset.h"
25 #include "URL.h"
26
27 CTextureDatabase::CTextureDatabase()
28 {
29 }
30
31 CTextureDatabase::~CTextureDatabase()
32 {
33 }
34
35 bool CTextureDatabase::Open()
36 {
37   return CDatabase::Open();
38 }
39
40 bool CTextureDatabase::CreateTables()
41 {
42   try
43   {
44     CDatabase::CreateTables();
45
46     CLog::Log(LOGINFO, "create texture table");
47     m_pDS->exec("CREATE TABLE texture (id integer primary key, url text, cachedurl text, imagehash text, lasthashcheck text)");
48
49     CLog::Log(LOGINFO, "create textures index");
50     m_pDS->exec("CREATE INDEX idxTexture ON texture(url)");
51
52     CLog::Log(LOGINFO, "create sizes table, index,  and trigger");
53     m_pDS->exec("CREATE TABLE sizes (idtexture integer, size integer, width integer, height integer, usecount integer, lastusetime text)");
54     m_pDS->exec("CREATE INDEX idxSize ON sizes(idtexture, size)");
55     m_pDS->exec("CREATE INDEX idxSize2 ON sizes(idtexture, width, height)");
56     m_pDS->exec("CREATE TRIGGER textureDelete AFTER delete ON texture FOR EACH ROW BEGIN delete from sizes where sizes.idtexture=old.id; END");
57
58     CLog::Log(LOGINFO, "create path table");
59     m_pDS->exec("CREATE TABLE path (id integer primary key, url text, type text, texture text)\n");
60
61     // TODO: Should the path index be a covering index? (we need only retrieve texture)
62     CLog::Log(LOGINFO, "create path index");
63     m_pDS->exec("CREATE INDEX idxPath ON path(url, type)");
64   }
65   catch (...)
66   {
67     CLog::Log(LOGERROR, "%s unable to create tables", __FUNCTION__);
68     return false;
69   }
70
71   return true;
72 }
73
74 bool CTextureDatabase::UpdateOldVersion(int version)
75 {
76   if (version < 7)
77   { // update all old thumb://foo urls to image://foo?size=thumb
78     m_pDS->query("select id,texture from path where texture like 'thumb://%'");
79     while (!m_pDS->eof())
80     {
81       unsigned int id = m_pDS->fv(0).get_asInt();
82       CURL url(m_pDS->fv(1).get_asString());
83       m_pDS2->exec(PrepareSQL("update path set texture='image://%s?size=thumb' where id=%u", url.GetHostName().c_str(), id));
84       m_pDS->next();
85     }
86     m_pDS->query("select id, url from texture where url like 'thumb://%'");
87     while (!m_pDS->eof())
88     {
89       unsigned int id = m_pDS->fv(0).get_asInt();
90       CURL url(m_pDS->fv(1).get_asString());
91       m_pDS2->exec(PrepareSQL("update texture set url='image://%s?size=thumb', urlhash=0 where id=%u", url.GetHostName().c_str(), id));
92       m_pDS->next();
93     }
94     m_pDS->close();
95   }
96   if (version < 8)
97   { // get rid of old cached thumbs as they were previously set to the cached thumb name instead of the source thumb
98     m_pDS->exec("delete from path");
99   }
100   if (version < 9)
101   { // get rid of the old path table and add the type column
102     m_pDS->dropIndex("path", "idxPath");
103     m_pDS->exec("DROP TABLE path");
104     m_pDS->exec("CREATE TABLE path (id integer primary key, urlhash integer, url text, type text, texture text)\n");
105     m_pDS->exec("CREATE INDEX idxPath ON path(urlhash, type)");
106   }
107   if (version < 10)
108   { // get rid of urlhash in both tables...
109     m_pDS->dropIndex("path", "idxPath");
110     m_pDS->exec("DROP TABLE path");
111     m_pDS->exec("CREATE TABLE path (id integer primary key, url text, type text, texture text)\n");
112     m_pDS->exec("CREATE INDEX idxPath ON path(url, type)");
113
114     m_pDS->dropIndex("texture", "idxTexture");
115     m_pDS->exec("CREATE TEMPORARY TABLE texture_backup(id,url,cachedurl,usecount,lastusetime,imagehash,lasthashcheck)");
116     m_pDS->exec("INSERT INTO texture_backup SELECT id,url,cachedurl,usecount,lastusetime,imagehash,lasthashcheck FROM texture");
117     m_pDS->exec("DROP TABLE texture");
118     m_pDS->exec("CREATE TABLE texture (id integer primary key, url text, cachedurl text, usecount integer, lastusetime text, imagehash text, lasthashcheck text)");
119     m_pDS->exec("CREATE INDEX idxTexture ON texture(url)");
120     m_pDS->exec("INSERT INTO texture SELECT * FROM texture_backup");
121     m_pDS->exec("DROP TABLE texture_backup");
122   }
123   if (version < 11)
124   { // get rid of cached URLs that don't have the correct extension
125     m_pDS->exec("DELETE FROM texture WHERE SUBSTR(cachedUrl,-4,4) NOT IN ('.jpg', '.png')");
126   }
127   if (version < 12)
128   { // create new sizes table and move usecount info to it.
129     m_pDS->exec("DROP TABLE texture");
130     m_pDS->exec("CREATE TABLE texture (id integer primary key, url text, cachedurl text, imagehash text, lasthashcheck text)");
131     m_pDS->exec("CREATE INDEX idxTexture ON texture(url)");
132     m_pDS->exec("CREATE TABLE sizes (idtexture integer, size integer, width integer, height integer, usecount integer, lastusetime text)");
133     m_pDS->exec("CREATE INDEX idxSize ON sizes(idtexture, size)");
134     m_pDS->exec("CREATE TRIGGER textureDelete AFTER delete ON texture FOR EACH ROW BEGIN delete from sizes where sizes.idtexture=old.id; END");
135   }
136   if (version < 13)
137   { // index for updateusecount
138     m_pDS->exec("CREATE INDEX idxSize2 ON sizes(idtexture, width, height)");
139   }
140   return true;
141 }
142
143 bool CTextureDatabase::IncrementUseCount(const CTextureDetails &details)
144 {
145   CStdString sql = PrepareSQL("UPDATE sizes SET usecount=usecount+1, lastusetime=CURRENT_TIMESTAMP WHERE idtexture=%u AND width=%u AND height=%u", details.id, details.width, details.height);
146   return ExecuteQuery(sql);
147 }
148
149 bool CTextureDatabase::GetCachedTexture(const CStdString &url, CTextureDetails &details)
150 {
151   try
152   {
153     if (NULL == m_pDB.get()) return false;
154     if (NULL == m_pDS.get()) return false;
155
156     CStdString sql = PrepareSQL("SELECT id, cachedurl, lasthashcheck, imagehash, width, height FROM texture JOIN sizes ON (texture.id=sizes.idtexture AND sizes.size=1) WHERE url='%s'", url.c_str());
157     m_pDS->query(sql.c_str());
158     if (!m_pDS->eof())
159     { // have some information
160       details.id = m_pDS->fv(0).get_asInt();
161       details.file  = m_pDS->fv(1).get_asString();
162       CDateTime lastCheck;
163       lastCheck.SetFromDBDateTime(m_pDS->fv(2).get_asString());
164       if (lastCheck.IsValid() && lastCheck + CDateTimeSpan(1,0,0,0) < CDateTime::GetCurrentDateTime())
165         details.hash = m_pDS->fv(3).get_asString();
166       details.width = m_pDS->fv(4).get_asInt();
167       details.height = m_pDS->fv(5).get_asInt();
168       m_pDS->close();
169       return true;
170     }
171     m_pDS->close();
172   }
173   catch (...)
174   {
175     CLog::Log(LOGERROR, "%s, failed on url '%s'", __FUNCTION__, url.c_str());
176   }
177   return false;
178 }
179
180 bool CTextureDatabase::SetCachedTextureValid(const CStdString &url, bool updateable)
181 {
182   CStdString date = updateable ? CDateTime::GetCurrentDateTime().GetAsDBDateTime() : "";
183   CStdString sql = PrepareSQL("UPDATE texture SET lasthashcheck='%s' WHERE url='%s'", date.c_str(), url.c_str());
184   return ExecuteQuery(sql);
185 }
186
187 bool CTextureDatabase::AddCachedTexture(const CStdString &url, const CTextureDetails &details)
188 {
189   try
190   {
191     if (NULL == m_pDB.get()) return false;
192     if (NULL == m_pDS.get()) return false;
193
194     CStdString sql = PrepareSQL("DELETE FROM texture WHERE url='%s'", url.c_str());
195     m_pDS->exec(sql.c_str());
196
197     CStdString date = details.updateable ? CDateTime::GetCurrentDateTime().GetAsDBDateTime() : "";
198     sql = PrepareSQL("INSERT INTO texture (id, url, cachedurl, imagehash, lasthashcheck) VALUES(NULL, '%s', '%s', '%s', '%s')", url.c_str(), details.file.c_str(), details.hash.c_str(), date.c_str());
199     m_pDS->exec(sql.c_str());
200     int textureID = (int)m_pDS->lastinsertid();
201
202     // set the size information
203     sql = PrepareSQL("INSERT INTO sizes (idtexture, size, usecount, lastusetime, width, height) VALUES(%u, 1, 1, CURRENT_TIMESTAMP, %u, %u)", textureID, details.width, details.height);
204     m_pDS->exec(sql.c_str());
205   }
206   catch (...)
207   {
208     CLog::Log(LOGERROR, "%s failed on url '%s'", __FUNCTION__, url.c_str());
209   }
210   return true;
211 }
212
213 bool CTextureDatabase::ClearCachedTexture(const CStdString &url, CStdString &cacheFile)
214 {
215   std::string id = GetSingleValue(PrepareSQL("select id from texture where url='%s'", url.c_str()));
216   return !id.empty() ? ClearCachedTexture(strtol(id.c_str(), NULL, 10), cacheFile) : false;
217 }
218
219 bool CTextureDatabase::ClearCachedTexture(int id, CStdString &cacheFile)
220 {
221   try
222   {
223     if (NULL == m_pDB.get()) return false;
224     if (NULL == m_pDS.get()) return false;
225
226     CStdString sql = PrepareSQL("select cachedurl from texture where id=%u", id);
227     m_pDS->query(sql.c_str());
228
229     if (!m_pDS->eof())
230     { // have some information
231       cacheFile = m_pDS->fv(0).get_asString();
232       m_pDS->close();
233       // remove it
234       sql = PrepareSQL("delete from texture where id=%u", id);
235       m_pDS->exec(sql.c_str());
236       return true;
237     }
238     m_pDS->close();
239   }
240   catch (...)
241   {
242     CLog::Log(LOGERROR, "%s, failed on texture id %u", __FUNCTION__, id);
243   }
244   return false;
245 }
246
247 bool CTextureDatabase::InvalidateCachedTexture(const CStdString &url)
248 {
249   CStdString date = (CDateTime::GetCurrentDateTime() - CDateTimeSpan(2, 0, 0, 0)).GetAsDBDateTime();
250   CStdString sql = PrepareSQL("UPDATE texture SET lasthashcheck='%s' WHERE url='%s'", date.c_str(), url.c_str());
251   return ExecuteQuery(sql);
252 }
253
254 CStdString CTextureDatabase::GetTextureForPath(const CStdString &url, const CStdString &type)
255 {
256   try
257   {
258     if (NULL == m_pDB.get()) return "";
259     if (NULL == m_pDS.get()) return "";
260
261     if (url.empty())
262       return "";
263
264     CStdString sql = PrepareSQL("select texture from path where url='%s' and type='%s'", url.c_str(), type.c_str());
265     m_pDS->query(sql.c_str());
266
267     if (!m_pDS->eof())
268     { // have some information
269       CStdString texture = m_pDS->fv(0).get_asString();
270       m_pDS->close();
271       return texture;
272     }
273     m_pDS->close();
274   }
275   catch (...)
276   {
277     CLog::Log(LOGERROR, "%s, failed on url '%s'", __FUNCTION__, url.c_str());
278   }
279   return "";
280 }
281
282 void CTextureDatabase::SetTextureForPath(const CStdString &url, const CStdString &type, const CStdString &texture)
283 {
284   try
285   {
286     if (NULL == m_pDB.get()) return;
287     if (NULL == m_pDS.get()) return;
288
289     if (url.empty())
290       return;
291
292     CStdString sql = PrepareSQL("select id from path where url='%s' and type='%s'", url.c_str(), type.c_str());
293     m_pDS->query(sql.c_str());
294     if (!m_pDS->eof())
295     { // update
296       int pathID = m_pDS->fv(0).get_asInt();
297       m_pDS->close();
298       sql = PrepareSQL("update path set texture='%s' where id=%u", texture.c_str(), pathID);
299       m_pDS->exec(sql.c_str());
300     }
301     else
302     { // add the texture
303       m_pDS->close();
304       sql = PrepareSQL("insert into path (id, url, type, texture) values(NULL, '%s', '%s', '%s')", url.c_str(), type.c_str(), texture.c_str());
305       m_pDS->exec(sql.c_str());
306     }
307   }
308   catch (...)
309   {
310     CLog::Log(LOGERROR, "%s failed on url '%s'", __FUNCTION__, url.c_str());
311   }
312   return;
313 }
314
315 void CTextureDatabase::ClearTextureForPath(const CStdString &url, const CStdString &type)
316 {
317   try
318   {
319     if (NULL == m_pDB.get()) return;
320     if (NULL == m_pDS.get()) return;
321
322     CStdString sql = PrepareSQL("DELETE FROM path WHERE url='%s' and type='%s'", url.c_str(), type.c_str());
323     m_pDS->exec(sql.c_str());
324   }
325   catch (...)
326   {
327     CLog::Log(LOGERROR, "%s failed on url '%s'", __FUNCTION__, url.c_str());
328   }
329   return;
330 }