db layer: call the lms_dlna_* functions
[platform/upstream/lightmediascanner.git] / src / lib / lightmediascanner_db_image.c
1 /**
2  * Copyright (C) 2008-2011 by ProFUSION embedded systems
3  * Copyright (C) 2007 by INdT
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA
19  *
20  * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
21  */
22
23 #include <lightmediascanner_db.h>
24 #include "lightmediascanner_db_private.h"
25 #include <lightmediascanner_dlna.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 struct lms_db_image {
30     sqlite3 *db;
31     sqlite3_stmt *insert;
32     unsigned int _references;
33     unsigned int _is_started:1;
34 };
35
36 static struct lms_db_cache _cache = { };
37
38 static int
39 _db_table_updater_images_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
40     char *errmsg;
41     int r, ret;
42
43     errmsg = NULL;
44     r = sqlite3_exec(db,
45                      "CREATE TABLE IF NOT EXISTS images ("
46                      "id INTEGER PRIMARY KEY, "
47                      "title TEXT, "
48                      "artist TEXT, "
49                      "date INTEGER NOT NULL, "
50                      "width INTEGER NOT NULL, "
51                      "height INTEGER NOT NULL, "
52                      "orientation INTEGER NOT NULL, "
53                      "gps_lat REAL DEFAULT 0.0, "
54                      "gps_long REAL DEFAULT 0.0, "
55                      "gps_alt REAL DEFAULT 0.0"
56                      ")",
57                      NULL, NULL, &errmsg);
58     if (r != SQLITE_OK) {
59         fprintf(stderr, "ERROR: could not create 'images' table: %s\n", errmsg);
60         sqlite3_free(errmsg);
61         return -1;
62     }
63
64     r = sqlite3_exec(db,
65                      "CREATE INDEX IF NOT EXISTS images_date_idx ON images ("
66                      "date"
67                      ")",
68                      NULL, NULL, &errmsg);
69     if (r != SQLITE_OK) {
70         fprintf(stderr, "ERROR: could not create 'images_date_idx' index: %s\n",
71                 errmsg);
72         sqlite3_free(errmsg);
73         return -2;
74     }
75
76     ret = lms_db_create_trigger_if_not_exists(db,
77         "delete_images_on_files_deleted "
78         "DELETE ON files FOR EACH ROW BEGIN "
79         " DELETE FROM images WHERE id = OLD.id; END;");
80     if (ret != 0)
81         goto done;
82
83     ret = lms_db_create_trigger_if_not_exists(db,
84         "delete_files_on_images_deleted "
85         "DELETE ON images FOR EACH ROW BEGIN "
86         " DELETE FROM files WHERE id = OLD.id; END;");
87
88   done:
89     return ret;
90 }
91
92 static int
93 _db_table_updater_images_1(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run)
94 {
95     int ret;
96     char *err;
97
98     ret = sqlite3_exec(
99         db, "BEGIN TRANSACTION;"
100         "ALTER TABLE images ADD COLUMN dlna_profile TEXT DEFAULT NULL;"
101         "ALTER TABLE images ADD COLUMN dlna_mime TEXT DEFAULT NULL;"
102         "COMMIT;",
103         NULL, NULL, &err);
104     if (ret != SQLITE_OK) {
105         fprintf(stderr, "ERROR: could add columns to images table: %s\n", err);
106         sqlite3_free(err);
107     }
108
109     return ret;
110 }
111
112 static int
113 _db_table_updater_images_2(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run)
114 {
115     int ret;
116     char *err;
117
118     ret = sqlite3_exec(
119         db, "BEGIN TRANSACTION;"
120         "ALTER TABLE images ADD COLUMN container TEXT DEFAULT NULL;"
121         "COMMIT;",
122         NULL, NULL, &err);
123     if (ret != SQLITE_OK) {
124         fprintf(stderr, "ERROR: could add columns to images table: %s\n", err);
125         sqlite3_free(err);
126     }
127
128     return ret;
129 }
130
131 static lms_db_table_updater_t _db_table_updater_images[] = {
132     _db_table_updater_images_0,
133     _db_table_updater_images_1,
134     _db_table_updater_images_2
135 };
136
137
138 static int
139 _db_create_table_if_required(sqlite3 *db)
140 {
141     return lms_db_table_update_if_required(db, "images",
142          LMS_ARRAY_SIZE(_db_table_updater_images),
143          _db_table_updater_images);
144 }
145
146 /**
147  * Create image DB access tool.
148  *
149  * Creates or get a reference to tools to access 'images' table in an
150  * optimized and easy way.
151  *
152  * This is usually called from plugin's @b setup() callback with the @p db
153  * got from @c ctxt.
154  *
155  * @param db database connection.
156  *
157  * @return DB access tool handle.
158  * @ingroup LMS_Plugins
159  */
160 lms_db_image_t *
161 lms_db_image_new(sqlite3 *db)
162 {
163     lms_db_image_t *ldi;
164     void *p;
165
166     if (lms_db_cache_get(&_cache, db, &p) == 0) {
167         ldi = p;
168         ldi->_references++;
169         return ldi;
170     }
171
172     if (!db)
173         return NULL;
174
175     if (_db_create_table_if_required(db) != 0) {
176         fprintf(stderr, "ERROR: could not create table.\n");
177         return NULL;
178     }
179
180     ldi = calloc(1, sizeof(lms_db_image_t));
181     ldi->_references = 1;
182     ldi->db = db;
183
184     if (lms_db_cache_add(&_cache, db, ldi) != 0) {
185         lms_db_image_free(ldi);
186         return NULL;
187     }
188
189     return ldi;
190 }
191
192 /**
193  * Start image DB access tool.
194  *
195  * Compile SQL statements and other initialization functions.
196  *
197  * This is usually called from plugin's @b start() callback.
198  *
199  * @param ldi handle returned by lms_db_image_new().
200  *
201  * @return On success 0 is returned.
202  * @ingroup LMS_Plugins
203  */
204 int
205 lms_db_image_start(lms_db_image_t *ldi)
206 {
207     if (!ldi)
208         return -1;
209     if (ldi->_is_started)
210         return 0;
211
212     ldi->insert = lms_db_compile_stmt(ldi->db,
213         "INSERT OR REPLACE INTO images ("
214         "id, title, artist, date, width, height, orientation, "
215         "gps_lat, gps_long, gps_alt, dlna_profile, dlna_mime, container) "
216          "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
217     if (!ldi->insert)
218         return -2;
219
220     ldi->_is_started = 1;
221     return 0;
222 }
223
224 /**
225  * Free image DB access tool.
226  *
227  * Unreference and possible free resources allocated to access tool.
228  *
229  * This is usually called from plugin's @b finish() callback.
230  *
231  * @param ldi handle returned by lms_db_image_new().
232  *
233  * @return On success 0 is returned.
234  * @ingroup LMS_Plugins
235  */
236 int
237 lms_db_image_free(lms_db_image_t *ldi)
238 {
239     int r;
240
241     if (!ldi)
242         return -1;
243     if (ldi->_references == 0) {
244         fprintf(stderr, "ERROR: over-called lms_db_image_free(%p)\n", ldi);
245         return -1;
246     }
247
248     ldi->_references--;
249     if (ldi->_references > 0)
250         return 0;
251
252     if (ldi->insert)
253         lms_db_finalize_stmt(ldi->insert, "insert");
254
255     r = lms_db_cache_del(&_cache, ldi->db, ldi);
256     free(ldi);
257
258     return r;
259 }
260
261 static int
262 _db_insert(lms_db_image_t *ldi, const struct lms_image_info *info)
263 {
264     sqlite3_stmt *stmt;
265     int r, ret;
266
267     stmt = ldi->insert;
268
269     ret = lms_db_bind_int64(stmt, 1, info->id);
270     if (ret != 0)
271         goto done;
272
273     ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
274     if (ret != 0)
275         goto done;
276
277     ret = lms_db_bind_text(stmt, 3, info->artist.str, info->artist.len);
278     if (ret != 0)
279         goto done;
280
281     ret = lms_db_bind_int(stmt, 4, info->date);
282     if (ret != 0)
283         goto done;
284
285     ret = lms_db_bind_int(stmt, 5, info->width);
286     if (ret != 0)
287         goto done;
288
289     ret = lms_db_bind_int(stmt, 6, info->height);
290     if (ret != 0)
291         goto done;
292
293     ret = lms_db_bind_int(stmt, 7, info->orientation);
294     if (ret != 0)
295         goto done;
296
297     ret = lms_db_bind_double(stmt, 8, info->gps.latitude);
298     if (ret != 0)
299         goto done;
300
301     ret = lms_db_bind_double(stmt, 9, info->gps.longitude);
302     if (ret != 0)
303         goto done;
304
305     ret = lms_db_bind_double(stmt, 10, info->gps.altitude);
306     if (ret != 0)
307         goto done;
308
309     ret = lms_db_bind_text(stmt, 11, info->dlna_profile.str,
310                            info->dlna_profile.len);
311     if (ret != 0)
312         goto done;
313
314     ret = lms_db_bind_text(stmt, 12, info->dlna_mime.str, info->dlna_mime.len);
315     if (ret != 0)
316         goto done;
317
318     ret = lms_db_bind_text(stmt, 13, info->container.str, info->container.len);
319     if (ret != 0)
320         goto done;
321
322     r = sqlite3_step(stmt);
323     if (r != SQLITE_DONE) {
324         fprintf(stderr, "ERROR: could not insert image info: %s\n",
325                 sqlite3_errmsg(ldi->db));
326         ret = -11;
327         goto done;
328     }
329
330     ret = 0;
331
332   done:
333     lms_db_reset_stmt(stmt);
334
335     return ret;
336 }
337
338 /**
339  * Add image file to DB.
340  *
341  * This is usually called from plugin's @b parse() callback.
342  *
343  * @param ldi handle returned by lms_db_image_new().
344  * @param info image information to store.
345  *
346  * @return On success 0 is returned.
347  * @ingroup LMS_Plugins
348  */
349 int
350 lms_db_image_add(lms_db_image_t *ldi, struct lms_image_info *info)
351 {
352     const struct lms_dlna_image_profile *dlna;
353
354     if (!ldi)
355         return -1;
356     if (!info)
357         return -2;
358     if (info->id < 1)
359         return -3;
360
361     if (info->dlna_mime.len == 0 && info->dlna_profile.len == 0) {
362         dlna = lms_dlna_get_image_profile(info);
363         if (dlna) {
364             info->dlna_mime = *dlna->dlna_mime;
365             info->dlna_profile = *dlna->dlna_profile;
366         }
367     }
368
369     return _db_insert(ldi, info);
370 }