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