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