05fe15485efba7f43d020572016cb6e28d320f34
[lms] / lightmediascanner / src / lib / lightmediascanner_db_video.c
1 #include <lightmediascanner_db.h>
2 #include "lightmediascanner_db_private.h"
3 #include <stdlib.h>
4 #include <stdio.h>
5
6 struct lms_db_video {
7     sqlite3 *db;
8     sqlite3_stmt *insert;
9     unsigned int _references;
10     unsigned int _is_started:1;
11 };
12
13 static struct lms_db_cache _cache = {0, NULL};
14
15 static int
16 _db_table_updater_videos_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
17     char *errmsg;
18     int r, ret;
19
20     errmsg = NULL;
21     r = sqlite3_exec(db,
22                      "CREATE TABLE IF NOT EXISTS videos ("
23                      "id INTEGER PRIMARY KEY, "
24                      "title TEXT, "
25                      "artist TEXT"
26                      ")",
27                      NULL, NULL, &errmsg);
28     if (r != SQLITE_OK) {
29         fprintf(stderr, "ERROR: could not create 'videos' table: %s\n", errmsg);
30         sqlite3_free(errmsg);
31         return -1;
32     }
33
34     r = sqlite3_exec(db,
35                      "CREATE INDEX IF NOT EXISTS videos_title_idx ON videos ("
36                      "title"
37                      ")",
38                      NULL, NULL, &errmsg);
39     if (r != SQLITE_OK) {
40         fprintf(stderr,
41                 "ERROR: could not create 'videos_title_idx' index: %s\n",
42                 errmsg);
43         sqlite3_free(errmsg);
44         return -2;
45     }
46
47     r = sqlite3_exec(db,
48                      "CREATE INDEX IF NOT EXISTS videos_artist_idx ON videos ("
49                      "artist"
50                      ")",
51                      NULL, NULL, &errmsg);
52     if (r != SQLITE_OK) {
53         fprintf(stderr,
54                 "ERROR: could not create 'videos_artist_idx' index: %s\n",
55                 errmsg);
56         sqlite3_free(errmsg);
57         return -3;
58     }
59
60     ret = lms_db_create_trigger_if_not_exists(db,
61         "delete_videos_on_files_deleted "
62         "DELETE ON files FOR EACH ROW BEGIN "
63         " DELETE FROM videos WHERE id = OLD.id; END;");
64     if (ret != 0)
65         goto done;
66
67     ret = lms_db_create_trigger_if_not_exists(db,
68         "delete_files_on_videos_deleted "
69         "DELETE ON videos FOR EACH ROW BEGIN "
70         " DELETE FROM files WHERE id = OLD.id; END;");
71
72   done:
73     return ret;
74 }
75
76 static lms_db_table_updater_t _db_table_updater_videos[] = {
77     _db_table_updater_videos_0
78 };
79
80
81 static int
82 _db_create_table_if_required(sqlite3 *db)
83 {
84     return lms_db_table_update_if_required(db, "videos",
85          LMS_ARRAY_SIZE(_db_table_updater_videos),
86          _db_table_updater_videos);
87 }
88
89 /**
90  * Create video DB access tool.
91  *
92  * Creates or get a reference to tools to access 'videos' table in an
93  * optimized and easy way.
94  *
95  * This is usually called from plugin's @b setup() callback with the @p db
96  * got from @c ctxt.
97  *
98  * @param db database connection.
99  *
100  * @return DB access tool handle.
101  * @ingroup LMS_Plugins
102  */
103 lms_db_video_t *
104 lms_db_video_new(sqlite3 *db)
105 {
106     lms_db_video_t *ldv;
107     void *p;
108
109     if (lms_db_cache_get(&_cache, db, &p) == 0) {
110         ldv = p;
111         ldv->_references++;
112         return ldv;
113     }
114
115     if (!db)
116         return NULL;
117
118     if (_db_create_table_if_required(db) != 0) {
119         fprintf(stderr, "ERROR: could not create table.\n");
120         return NULL;
121     }
122
123     ldv = calloc(1, sizeof(lms_db_video_t));
124     ldv->_references = 1;
125     ldv->db = db;
126
127     if (lms_db_cache_add(&_cache, db, ldv) != 0) {
128         lms_db_video_free(ldv);
129         return NULL;
130     }
131
132     return ldv;
133 }
134
135 /**
136  * Start video DB access tool.
137  *
138  * Compile SQL statements and other initialization functions.
139  *
140  * This is usually called from plugin's @b start() callback.
141  *
142  * @param ldv handle returned by lms_db_video_new().
143  *
144  * @return On success 0 is returned.
145  * @ingroup LMS_Plugins
146  */
147 int
148 lms_db_video_start(lms_db_video_t *ldv)
149 {
150     if (!ldv)
151         return -1;
152     if (ldv->_is_started)
153         return 0;
154
155     ldv->insert = lms_db_compile_stmt(ldv->db,
156         "INSERT OR REPLACE INTO videos (id, title, artist) VALUES (?, ?, ?)");
157     if (!ldv->insert)
158         return -2;
159
160     ldv->_is_started = 1;
161     return 0;
162 }
163
164 /**
165  * Free video DB access tool.
166  *
167  * Unreference and possible free resources allocated to access tool.
168  *
169  * This is usually called from plugin's @b finish() callback.
170  *
171  * @param ldv handle returned by lms_db_video_new().
172  *
173  * @return On success 0 is returned.
174  * @ingroup LMS_Plugins
175  */
176 int
177 lms_db_video_free(lms_db_video_t *ldv)
178 {
179     int r;
180
181     if (!ldv)
182         return -1;
183     if (ldv->_references == 0) {
184         fprintf(stderr, "ERROR: over-called lms_db_video_free(%p)\n", ldv);
185         return -1;
186     }
187
188     ldv->_references--;
189     if (ldv->_references > 0)
190         return 0;
191
192     if (ldv->insert)
193         lms_db_finalize_stmt(ldv->insert, "insert");
194
195     r = lms_db_cache_del(&_cache, ldv->db, ldv);
196     free(ldv);
197
198     return r;
199 }
200
201 static int
202 _db_insert(lms_db_video_t *ldv, const struct lms_video_info *info)
203 {
204     sqlite3_stmt *stmt;
205     int r, ret;
206
207     stmt = ldv->insert;
208
209     ret = lms_db_bind_int64(stmt, 1, info->id);
210     if (ret != 0)
211         goto done;
212
213     ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
214     if (ret != 0)
215         goto done;
216
217     ret = lms_db_bind_text(stmt, 3, info->artist.str, info->artist.len);
218     if (ret != 0)
219         goto done;
220
221     r = sqlite3_step(stmt);
222     if (r != SQLITE_DONE) {
223         fprintf(stderr, "ERROR: could not insert video info: %s\n",
224                 sqlite3_errmsg(ldv->db));
225         ret = -4;
226         goto done;
227     }
228
229     ret = 0;
230
231   done:
232     lms_db_reset_stmt(stmt);
233
234     return ret;
235 }
236
237 /**
238  * Add video file to DB.
239  *
240  * This is usually called from plugin's @b parse() callback.
241  *
242  * @param ldv handle returned by lms_db_video_new().
243  * @param info video information to store.
244  *
245  * @return On success 0 is returned.
246  * @ingroup LMS_Plugins
247  */
248 int
249 lms_db_video_add(lms_db_video_t *ldv, struct lms_video_info *info)
250 {
251     if (!ldv)
252         return -1;
253     if (!info)
254         return -2;
255     if (info->id < 1)
256         return -3;
257
258     return _db_insert(ldv, info);
259 }