1 /*****************************************************************
2 * gmerlin - a general purpose multimedia framework and applications
3 *
4 * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5 * gmerlin-general@lists.sourceforge.net
6 * http://gmerlin.sourceforge.net
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * *****************************************************************/
21
22 /* Song */
23
24 #include "nmjedit.h"
25 #include <string.h>
26 #include <unistd.h>
27 #include <gmerlin/utils.h>
28 #include <gmerlin/translation.h>
29 #include <gmerlin/log.h>
30 #define LOG_DOMAIN "nmjedit.song"
31
32 #include <gavl/metatags.h>
33
bg_nmj_song_free(bg_nmj_song_t * song)34 void bg_nmj_song_free(bg_nmj_song_t * song)
35 {
36 MY_FREE(song->title);
37 MY_FREE(song->search_title);
38 MY_FREE(song->path);
39 MY_FREE(song->runtime);
40 MY_FREE(song->format);
41 MY_FREE(song->lyric);
42 MY_FREE(song->hash);
43 MY_FREE(song->bit_rate);
44 MY_FREE(song->release_date);
45 MY_FREE(song->create_time);
46 MY_FREE(song->update_state);
47 MY_FREE(song->filestatus);
48 MY_FREE(song->artist);
49 MY_FREE(song->genre);
50 MY_FREE(song->albumartist);
51 MY_FREE(song->album);
52 }
53
bg_nmj_song_init(bg_nmj_song_t * song)54 void bg_nmj_song_init(bg_nmj_song_t * song)
55 {
56 memset(song, 0, sizeof(*song));
57 song->id = -1;
58 song->album_id = -1;
59 song->artist_id = -1;
60 song->albumartist_id = -1;
61 song->genre_id = -1;
62 }
63
bg_nmj_song_dump(bg_nmj_song_t * song)64 void bg_nmj_song_dump(bg_nmj_song_t * song)
65 {
66 bg_dprintf("Song:\n");
67 bg_dprintf(" ID: %"PRId64"\n", song->id);
68 bg_dprintf(" title: %s\n", song->title);
69 bg_dprintf(" search_title: %s\n", song->search_title);
70 bg_dprintf(" path: %s\n", song->path);
71 bg_dprintf(" scan_dirs_id: %"PRId64"\n", song->scan_dirs_id);
72 bg_dprintf(" folders_id: %"PRId64"\n", song->folders_id);
73 bg_dprintf(" runtime: %s\n", song->runtime);
74 bg_dprintf(" format: %s\n", song->format);
75 bg_dprintf(" lyric: %s\n", song->lyric);
76 bg_dprintf(" rating: %"PRId64"\n", song->rating);
77 bg_dprintf(" hash: %s\n", song->hash);
78 bg_dprintf(" size: %"PRId64"\n", song->size);
79 bg_dprintf(" bit_rate: %s\n", song->bit_rate);
80 bg_dprintf(" track_position: %"PRId64"\n", song->track_position);
81 bg_dprintf(" release_date: %s\n", song->release_date);
82 bg_dprintf(" create_time: %s\n", song->create_time);
83 bg_dprintf(" update_state: %s\n", song->update_state);
84 bg_dprintf(" filestatus: %s\n", song->filestatus);
85 bg_dprintf(" album: %s\n", song->album);
86 bg_dprintf(" genre: %s\n", song->genre);
87 bg_dprintf(" artist: %s\n", song->artist);
88 bg_dprintf(" albumartist: %s\n", song->albumartist);
89 bg_dprintf(" genre_id: %"PRId64"\n", song->genre_id);
90 bg_dprintf(" artist_id: %"PRId64"\n", song->artist_id);
91 bg_dprintf(" albumartist_id: %"PRId64"\n", song->albumartist_id);
92 bg_dprintf(" album_id: %"PRId64"\n", song->album_id);
93 }
94
song_query_callback(void * data,int argc,char ** argv,char ** azColName)95 static int song_query_callback(void * data, int argc, char **argv, char **azColName)
96 {
97 int i;
98 bg_nmj_song_t * ret = data;
99
100 for(i = 0; i < argc; i++)
101 {
102 // fprintf(stderr, "col: %s, val: %s\n", azColName[i], argv[i]);
103 SET_QUERY_INT("ID", id);
104 SET_QUERY_STRING("TITLE", title);
105 SET_QUERY_STRING("SEARCH_TITLE", search_title);
106 SET_QUERY_STRING("PATH", path);
107 SET_QUERY_INT("SCAN_DIRS_ID", scan_dirs_id);
108 SET_QUERY_INT("FOLDERS_ID", folders_id);
109 SET_QUERY_STRING("RUNTIME", runtime);
110 SET_QUERY_STRING("FORMAT", format);
111 SET_QUERY_STRING("LYRIC", lyric);
112 SET_QUERY_INT("RATING", rating);
113 SET_QUERY_STRING("HASH", hash);
114 SET_QUERY_INT("SIZE", size);
115 SET_QUERY_STRING("BIT_RATE", bit_rate);
116 SET_QUERY_INT("TRACK_POSITION", track_position);
117 SET_QUERY_STRING("RELEASE_DATE", release_date);
118 SET_QUERY_STRING("CREATE_TIME", create_time);
119 SET_QUERY_STRING("UPDATE_STATE", update_state );
120 SET_QUERY_STRING("FILESTATUS", filestatus );
121 }
122 ret->found = 1;
123 return 0;
124 }
125
126
bg_nmj_song_query(sqlite3 * db,bg_nmj_song_t * song)127 int bg_nmj_song_query(sqlite3 * db, bg_nmj_song_t * song)
128 {
129 char * sql;
130 int result;
131 if(song->path)
132 {
133 sql = sqlite3_mprintf("select * from SONGS where PATH = %Q;", song->path);
134 result = bg_sqlite_exec(db, sql, song_query_callback, song);
135 sqlite3_free(sql);
136 if(!song->found)
137 return 0;
138 }
139 else if(song->id >= 0)
140 {
141 sql = sqlite3_mprintf("select * from SONGS where ID = %"PRId64";", song->id);
142 result = bg_sqlite_exec(db, sql, song_query_callback, song);
143 sqlite3_free(sql);
144 if(!song->found)
145 return 0;
146 }
147 else
148 {
149 bg_log(BG_LOG_ERROR, LOG_DOMAIN,
150 "Either ID or path must be set in directory");
151 return 0;
152 }
153
154 /* Get secondary stuff */
155 song->genre_id = bg_nmj_id_to_id(db, "SONG_GENRES_SONGS",
156 "GENRES_ID", "SONGS_ID", song->id);
157 if(song->genre_id >= 0)
158 song->genre = bg_nmj_id_to_string(db, "SONG_GENRES",
159 "NAME", "ID", song->genre_id);
160
161 song->album_id = bg_nmj_id_to_id(db, "SONG_ALBUMS_SONGS",
162 "ALBUMS_ID", "SONGS_ID", song->id);
163 if(song->album_id >= 0)
164 song->album = bg_nmj_id_to_string(db, "SONG_ALBUMS",
165 "TITLE", "ID", song->album_id);
166
167 song->artist_id = bg_nmj_id_to_id(db, "SONG_PERSONS_SONGS",
168 "PERSONS_ID", "SONGS_ID", song->id);
169 if(song->artist_id >= 0)
170 song->artist = bg_nmj_id_to_string(db, "SONG_PERSONS",
171 "NAME", "ID", song->artist_id);
172 return 1;
173 }
174
bg_nmj_song_get_info(sqlite3 * db,bg_plugin_registry_t * plugin_reg,bg_nmj_dir_t * dir,bg_nmj_file_t * file,bg_nmj_song_t * song)175 int bg_nmj_song_get_info(sqlite3 * db,
176 bg_plugin_registry_t * plugin_reg,
177 bg_nmj_dir_t * dir,
178 bg_nmj_file_t * file,
179 bg_nmj_song_t * song)
180 {
181 bg_plugin_handle_t * h = NULL;
182 bg_input_plugin_t * plugin = NULL;
183 int ret = 0;
184 bg_track_info_t * ti;
185 int year;
186 int tag_i;
187
188 if(!bg_input_plugin_load(plugin_reg, file->path, NULL, &h, NULL, 0))
189 goto fail;
190
191 plugin = (bg_input_plugin_t *)h->plugin;
192
193 /* Only one track supported */
194 if(plugin->get_num_tracks && (plugin->get_num_tracks(h->priv) < 1))
195 goto fail;
196
197 ti = plugin->get_track_info(h->priv, 0);
198
199 if(!ti->num_audio_streams)
200 goto fail;
201
202 if(plugin->set_track && !plugin->set_track(h->priv, 0))
203 goto fail;
204
205 if(!plugin->set_audio_stream(h->priv, 0, BG_STREAM_ACTION_DECODE))
206 goto fail;
207
208 if(!plugin->start(h->priv))
209 goto fail;
210
211 /* Fill in the data structure */
212
213 song->title = bg_nmj_escape_string(gavl_metadata_get(&ti->metadata, GAVL_META_TITLE));
214 song->search_title = bg_nmj_make_search_string(song->title);
215
216 song->path = bg_strdup(song->path, file->path);
217 song->scan_dirs_id = dir->id;
218 song->runtime = bg_sprintf("%d", (int)(ti->duration / GAVL_TIME_SCALE));
219
220 song->format = bg_strdup(song->format,
221 gavl_metadata_get(&ti->metadata, GAVL_META_FORMAT));
222 song->size = file->size;
223
224 if(gavl_metadata_get_int(&ti->audio_streams[0].m, GAVL_META_BITRATE, &tag_i))
225 song->bit_rate = bg_sprintf("%d", (int)(tag_i / 1000));
226 else
227 song->bit_rate = bg_strdup(song->bit_rate, "VBR");
228
229 if(gavl_metadata_get_int(&ti->metadata, GAVL_META_TRACKNUMBER, &tag_i))
230 song->track_position = tag_i;
231
232 year = bg_metadata_get_year(&ti->metadata);
233 if(year)
234 song->release_date = bg_sprintf("%d-01-01", year);
235 else
236 song->release_date = bg_strdup(NULL, "9999-01-01");
237 song->create_time = malloc(BG_NMJ_TIME_STRING_LEN);
238 bg_nmj_time_to_string(file->time, song->create_time);
239
240 song->album =
241 bg_nmj_escape_string(gavl_metadata_get(&ti->metadata, GAVL_META_ALBUM));
242 song->artist =
243 bg_nmj_escape_string(gavl_metadata_get(&ti->metadata, GAVL_META_ARTIST));
244 song->albumartist =
245 bg_nmj_escape_string(gavl_metadata_get(&ti->metadata, GAVL_META_ALBUMARTIST));
246 song->genre =
247 bg_nmj_escape_string(gavl_metadata_get(&ti->metadata, GAVL_META_GENRE));
248
249 /* Unknown stuff */
250 song->update_state = bg_sprintf("%d", 2);
251
252 /* Get IDs */
253 if(song->genre)
254 song->genre_id = bg_nmj_string_to_id(db, "SONG_GENRES", "ID",
255 "NAME", song->genre);
256
257 if(song->albumartist)
258 song->albumartist_id = bg_nmj_string_to_id(db, "SONG_PERSONS", "ID",
259 "NAME", song->albumartist);
260 if(song->artist)
261 song->artist_id = bg_nmj_string_to_id(db, "SONG_PERSONS", "ID",
262 "NAME", song->artist);
263
264 /* Lookup album (title AND albumartist MUST match) */
265 if(song->album && (song->albumartist_id >= 0))
266 song->album_id = bg_nmj_album_lookup(db, song->albumartist_id, song->album);
267
268 ret = 1;
269 fail:
270
271 if(plugin)
272 plugin->close(h->priv);
273
274 if(h)
275 bg_plugin_unref(h);
276 return ret;
277 }
278
bg_nmj_song_add(bg_plugin_registry_t * plugin_reg,sqlite3 * db,bg_nmj_song_t * song)279 int bg_nmj_song_add(bg_plugin_registry_t * plugin_reg,
280 sqlite3 * db, bg_nmj_song_t * song)
281 {
282 int result;
283 char * sql;
284 int64_t id;
285
286 bg_log(BG_LOG_INFO, LOG_DOMAIN, "Adding song %s", song->title);
287
288 /* Get ID */
289 song->id = bg_nmj_get_next_id(db, "SONGS");
290
291 /* Add Song */
292 sql = sqlite3_mprintf("INSERT INTO SONGS "
293 "( ID, TITLE, SEARCH_TITLE, PATH, SCAN_DIRS_ID, "
294 "RUNTIME, FORMAT, RATING, SIZE, PLAY_COUNT, BIT_RATE, "
295 "TRACK_POSITION, RELEASE_DATE, CREATE_TIME, UPDATE_STATE ) VALUES "
296 "( %"PRId64", %Q, %Q, %Q, %"PRId64","
297 " %Q, %Q, %"PRId64", %"PRId64", %"PRId64", %Q, "
298 " %"PRId64", %Q, %Q, %Q);",
299 song->id, song->title, song->search_title, song->path, song->scan_dirs_id,
300 song->runtime, song->format, song->rating, song->size, "0", song->bit_rate,
301 song->track_position, song->release_date, song->create_time, song->update_state);
302
303 result = bg_sqlite_exec(db, sql, NULL, NULL);
304 sqlite3_free(sql);
305 if(!result)
306 return result;
307
308 if(song->genre)
309 {
310 /* Add new genre */
311 if(song->genre_id < 0)
312 {
313 song->genre_id = bg_nmj_get_next_id(db, "SONG_GENRES");
314 sql = sqlite3_mprintf("INSERT INTO SONG_GENRES ( ID, NAME ) VALUES ( %"PRId64", %Q );",
315 song->genre_id, song->genre);
316 result = bg_sqlite_exec(db, sql, NULL, NULL);
317 sqlite3_free(sql);
318 if(!result)
319 return result;
320 }
321 /* Set Genre */
322 id = bg_nmj_get_next_id(db, "SONG_GENRES_SONGS");
323 sql = sqlite3_mprintf("INSERT INTO SONG_GENRES_SONGS ( ID, SONGS_ID, GENRES_ID ) VALUES "
324 "( %"PRId64", %"PRId64", %"PRId64" );",
325 id, song->id, song->genre_id);
326 result = bg_sqlite_exec(db, sql, NULL, NULL);
327 sqlite3_free(sql);
328 if(!result)
329 return result;
330 }
331
332 if(song->artist)
333 {
334 /* Add new artist */
335 if(song->artist_id < 0)
336 {
337 song->artist_id = bg_nmj_get_next_id(db, "SONG_PERSONS");
338 sql = sqlite3_mprintf("INSERT INTO SONG_PERSONS ( ID, NAME ) VALUES ( %"PRId64", %Q );",
339 song->artist_id, song->artist);
340 result = bg_sqlite_exec(db, sql, NULL, NULL);
341 sqlite3_free(sql);
342 if(!result)
343 return result;
344 }
345
346 /* Set artist */
347 id = bg_nmj_get_next_id(db, "SONG_PERSONS_SONGS");
348 sql = sqlite3_mprintf("INSERT INTO SONG_PERSONS_SONGS ( ID, PERSONS_ID, SONGS_ID, PERSON_TYPE ) VALUES "
349 "( %"PRId64", %"PRId64", %"PRId64", %Q );",
350 id, song->artist_id, song->id, "ARTIST");
351 result = bg_sqlite_exec(db, sql, NULL, NULL);
352 sqlite3_free(sql);
353 if(!result)
354 return result;
355 }
356
357 if(song->albumartist)
358 {
359 if(!strcmp(song->artist, song->albumartist))
360 song->albumartist_id = song->artist_id;
361
362 /* Add new album artist */
363 if(song->albumartist_id < 0)
364 {
365 song->albumartist_id = bg_nmj_get_next_id(db, "SONG_PERSONS");
366 sql =
367 sqlite3_mprintf("INSERT INTO SONG_PERSONS ( ID, NAME ) VALUES ( %"PRId64", %Q );",
368 song->albumartist_id, song->albumartist);
369 result = bg_sqlite_exec(db, sql, NULL, NULL);
370 sqlite3_free(sql);
371 if(!result)
372 return result;
373 }
374 }
375
376 /* Album */
377
378 if(song->album && song->albumartist && (song->track_position > 0))
379 {
380 bg_nmj_album_t album;
381 bg_nmj_album_init(&album);
382 album.id = song->album_id;
383 bg_nmj_album_add(plugin_reg, db, &album, song);
384 bg_nmj_album_free(&album);
385 }
386
387 return 1;
388 }
389
bg_nmj_song_get_cover(bg_nmj_song_t * song)390 char * bg_nmj_song_get_cover(bg_nmj_song_t * song)
391 {
392 char * pos;
393 char * ret;
394 char * directory = bg_strdup(NULL, song->path);
395 pos = strrchr(directory, '/');
396 if(!pos)
397 {
398 free(directory);
399 return NULL;
400 }
401 *pos = '\0';
402 ret = bg_sprintf("%s/cover.jpg", directory);
403 if(!access(ret, R_OK | W_OK))
404 {
405 free(directory);
406 return ret;
407 }
408 free(directory);
409 free(ret);
410 return NULL;
411 }
412
413 #define APPEND_STRING(member, col) \
414 if(old_song->member && new_song->member && \
415 strcmp(old_song->member, new_song->member)) \
416 { \
417 if(num_cols) \
418 sql = bg_strcat(sql, ","); \
419 tmp_string = sqlite3_mprintf(" " col " = %Q", new_song->member); \
420 sql = bg_strcat(sql, tmp_string); \
421 sqlite3_free(tmp_string); \
422 num_cols++; \
423 }
424
425 #define APPEND_INT(member, col) \
426 if(old_song->member != new_song->member) \
427 { \
428 if(num_cols) \
429 sql = bg_strcat(sql, ","); \
430 tmp_string = sqlite3_mprintf(" " col " = %"PRId64, new_song->member); \
431 sql = bg_strcat(sql, tmp_string); \
432 sqlite3_free(tmp_string); \
433 num_cols++; \
434 }
435
436
bg_nmj_song_delete(sqlite3 * db,bg_nmj_song_t * song)437 int bg_nmj_song_delete(sqlite3 * db, bg_nmj_song_t * song)
438 {
439 char * sql;
440 int result;
441 /* Delete from SONGS */
442 bg_log(BG_LOG_INFO, LOG_DOMAIN, "Deleting song %s", song->title);
443
444 sql = sqlite3_mprintf("DELETE FROM SONGS WHERE ID = %"PRId64";", song->id);
445 result = bg_sqlite_exec(db, sql, NULL, NULL);
446 sqlite3_free(sql);
447 if(!result)
448 return 0;
449
450 if(song->album_id >= 0)
451 {
452 /* Delete from album */
453 if(!bg_nmj_album_delete(db, song->album_id, song))
454 return 0;
455 }
456
457 /* Delete from SONG_GENRES_SONGS */
458 if(song->genre_id >= 0)
459 {
460 sql = sqlite3_mprintf("DELETE FROM SONG_GENRES_SONGS WHERE SONGS_ID = %"PRId64";",
461 song->id);
462 result = bg_sqlite_exec(db, sql, NULL, NULL);
463 sqlite3_free(sql);
464 if(!result)
465 return 0;
466 sql = sqlite3_mprintf("DELETE FROM SONG_GENRES_SONGS WHERE SONGS_ID = %"PRId64";",
467 song->id);
468 result = bg_sqlite_exec(db, sql, NULL, NULL);
469 sqlite3_free(sql);
470 if(!result)
471 return 0;
472 }
473
474 /* Delete from SONG_PERSONS_SONGS */
475 if(song->artist_id >= 0)
476 {
477 sql = sqlite3_mprintf("DELETE FROM SONG_PERSONS_SONGS WHERE SONGS_ID = %"PRId64";",
478 song->id);
479 result = bg_sqlite_exec(db, sql, NULL, NULL);
480 sqlite3_free(sql);
481 if(!result)
482 return 0;
483 }
484 return 1;
485 }
486