From 298961916acac828df8de8ac740eacc2df0a5e40 Mon Sep 17 00:00:00 2001 From: waker Date: Wed, 9 Mar 2011 14:00:48 +0100 Subject: few fixes/improvements to metadata editor and related plugin APIs --- deadbeef.h | 9 ++-- junklib.c | 51 +++++++++++++----- playlist.c | 50 +++++++++++++++-- playlist.h | 8 ++- plugins.c | 4 +- plugins/gtkui/trkproperties.c | 122 +++++++++++++++++++++++++++++------------- 6 files changed, 185 insertions(+), 59 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index d9fb9d81..3a8bff04 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -440,18 +440,21 @@ typedef struct { void (*pl_copy_items) (int iter, int plt_from, DB_playItem_t *before, uint32_t *indices, int cnt); void (*pl_search_reset) (void); void (*pl_search_process) (const char *text); - // metainfo + // direct access to metadata structures + DB_metaInfo_t * (*pl_get_metadata_head) (DB_playItem_t *it); // returns head of metadata linked list + void (*pl_delete_metadata) (DB_playItem_t *it, DB_metaInfo_t *meta); + + // high-level access to metadata void (*pl_add_meta) (DB_playItem_t *it, const char *key, const char *value); void (*pl_append_meta) (DB_playItem_t *it, const char *key, const char *value); void (*pl_set_meta_int) (DB_playItem_t *it, const char *key, int value); void (*pl_set_meta_float) (DB_playItem_t *it, const char *key, float value); - // must be used from within explicit pl_lock/unlock block + void (*pl_delete_meta) (DB_playItem_t *it, const char *key); const char *(*pl_find_meta) (DB_playItem_t *it, const char *key); int (*pl_find_meta_int) (DB_playItem_t *it, const char *key, int def); float (*pl_find_meta_float) (DB_playItem_t *it, const char *key, float def); void (*pl_replace_meta) (DB_playItem_t *it, const char *key, const char *value); void (*pl_delete_all_meta) (DB_playItem_t *it); - DB_metaInfo_t * (*pl_get_metadata) (DB_playItem_t *it); void (*pl_set_item_duration) (DB_playItem_t *it, float duration); float (*pl_get_item_duration) (DB_playItem_t *it); uint32_t (*pl_get_item_flags) (DB_playItem_t *it); diff --git a/junklib.c b/junklib.c index 87dd59f6..667bcdcb 100644 --- a/junklib.c +++ b/junklib.c @@ -1627,6 +1627,28 @@ junk_id3v2_remove_txxx_frame (DB_id3v2_tag_t *tag, const char *key) { return 0; } +int +junk_id3v2_remove_all_txxx_frames (DB_id3v2_tag_t *tag) { + DB_id3v2_frame_t *prev = NULL; + for (DB_id3v2_frame_t *f = tag->frames; f; ) { + DB_id3v2_frame_t *next = f->next; + if (!strcmp (f->id, "TXXX")) { + if (prev) { + prev->next = f->next; + } + else { + tag->frames = f->next; + } + free (f); + } + else { + prev = f; + } + f = next; + } + return 0; +} + DB_id3v2_frame_t * junk_id3v2_add_txxx_frame (DB_id3v2_tag_t *tag, const char *key, const char *value) { int keylen = strlen (key); @@ -3348,7 +3370,7 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const int write_apev2 = junk_flags & JUNK_WRITE_APEV2; // find the beginning and the end of audio data - fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); + fp = deadbeef->fopen (pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -3398,7 +3420,7 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const // open output file out = NULL; char tmppath[PATH_MAX]; - snprintf (tmppath, sizeof (tmppath), "%s.temp", deadbeef->pl_find_meta (it, ":URI")); + snprintf (tmppath, sizeof (tmppath), "%s.temp", pl_find_meta (it, ":URI")); out = fopen (tmppath, "w+b"); trace ("will write tags into %s\n", tmppath); @@ -3471,11 +3493,7 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const } } - // remove all known txxx frames - for (int txx = 0; txx_mapping[txx]; txx += 2) { - trace ("removing txxx %s\n", txx_mapping[txx]) - junk_id3v2_remove_txxx_frame (&id3v2, txx_mapping[txx]); - } + junk_id3v2_remove_all_txxx_frames (&id3v2); // COMM junk_id3v2_remove_frames (&id3v2, "COMM"); @@ -3485,7 +3503,16 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const junk_id3v2_add_comment_frame (&id3v2, "eng", "", val); } - DB_metaInfo_t *meta = pl_get_metadata (it); + // remove all known normal frames (they will be refilled from track metadata) + int idx = id3v2.version[0] == 3 ? MAP_ID3V23 : MAP_ID3V24; + for (int i = 0; frame_mapping[i]; i += FRAME_MAPPINGS) { + if (frame_mapping[i+idx]) { + junk_id3v2_remove_frames (&id3v2, frame_mapping[i+idx]); + trace ("removed frame %s\n", frame_mapping[i+idx]); + } + } + + DB_metaInfo_t *meta = pl_get_metadata_head (it); while (meta) { if (meta->value && *meta->value) { int i; @@ -3494,10 +3521,8 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const const char *frm_name = id3v2_version == 3 ? frame_mapping[i+MAP_ID3V23] : frame_mapping[i+MAP_ID3V24]; if (frm_name) { // field is known and supported for this tag version - junk_id3v2_remove_frames (&id3v2, frm_name); trace ("add_frame %s %s\n", frm_name, meta->value); junk_id3v2_add_text_frame (&id3v2, frm_name, meta->value); - //junk_id3v2_add_text_frame (&id3v2, frm_name, "test line 1\nтестовая строка №2"); } break; } @@ -3508,7 +3533,7 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const && strcasecmp (meta->key, "track") && strcasecmp (meta->key, "numtracks")) { // add as txxx - trace ("adding TXX %s=%s\n", meta->key, meta->value); + trace ("adding unknown frame as TXX %s=%s\n", meta->key, meta->value); junk_id3v2_remove_txxx_frame (&id3v2, meta->key); junk_id3v2_add_txxx_frame (&id3v2, meta->key, meta->value); } @@ -3594,7 +3619,7 @@ junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const memset (&apev2, 0, sizeof (apev2)); } // add all basic frames - DB_metaInfo_t *meta = pl_get_metadata (it); + DB_metaInfo_t *meta = pl_get_metadata_head (it); while (meta) { if (meta->value && *meta->value) { int i; @@ -3675,7 +3700,7 @@ error: free (buffer); } if (!err) { - rename (tmppath, deadbeef->pl_find_meta (it, ":URI")); + rename (tmppath, pl_find_meta (it, ":URI")); } else { unlink (tmppath); diff --git a/playlist.c b/playlist.c index fc7fb6ee..74dcd1bb 100644 --- a/playlist.c +++ b/playlist.c @@ -1987,8 +1987,8 @@ pl_replace_meta (playItem_t *it, const char *key, const char *value) { m = m->next; } if (m) { - metacache_remove_string (m->value);//free (m->value); - m->value = metacache_add_string (value);//strdup (value); + metacache_remove_string (m->value); + m->value = metacache_add_string (value); UNLOCK; return; } @@ -2012,6 +2012,28 @@ pl_set_meta_float (playItem_t *it, const char *key, float value) { pl_replace_meta (it, key, s); } +void +pl_delete_meta (playItem_t *it, const char *key) { + DB_metaInfo_t *prev = NULL; + DB_metaInfo_t *m = it->meta; + while (m) { + if (!strcasecmp (key, m->key)) { + if (prev) { + prev->next = m->next; + } + else { + it->meta = m->next; + } + metacache_remove_string (m->key); + metacache_remove_string (m->value); + free (m); + break; + } + prev = m; + m = m->next; + } +} + const char * pl_find_meta (playItem_t *it, const char *key) { DB_metaInfo_t *m = it->meta; @@ -2037,10 +2059,32 @@ pl_find_meta_float (playItem_t *it, const char *key, float def) { } DB_metaInfo_t * -pl_get_metadata (playItem_t *it) { +pl_get_metadata_head (playItem_t *it) { return it->meta; } +void +pl_delete_metadata (playItem_t *it, DB_metaInfo_t *meta) { + DB_metaInfo_t *prev = NULL; + DB_metaInfo_t *m = it->meta; + while (m) { + if (m == meta) { + if (prev) { + prev->next = m->next; + } + else { + it->meta = m->next; + } + metacache_remove_string (m->key); + metacache_remove_string (m->value); + free (m); + break; + } + prev = m; + m = m->next; + } +} + int pl_delete_selected (void) { GLOBAL_LOCK; diff --git a/playlist.h b/playlist.h index 11e3cb63..1a13ef8d 100644 --- a/playlist.h +++ b/playlist.h @@ -232,6 +232,9 @@ pl_set_meta_int (playItem_t *it, const char *key, int value); void pl_set_meta_float (playItem_t *it, const char *key, float value); +void +pl_delete_meta (playItem_t *it, const char *key); + void pl_delete_all_meta (playItem_t *it); @@ -370,7 +373,10 @@ void pl_items_copy_junk (struct playItem_s *from, struct playItem_s *first, struct playItem_s *last); struct DB_metaInfo_s * -pl_get_metadata (playItem_t *it); +pl_get_metadata_head (playItem_t *it); + +void +pl_delete_metadata (playItem_t *it, struct DB_metaInfo_s *meta); void pl_set_order (int order); diff --git a/plugins.c b/plugins.c index fedba88b..b3142e4e 100644 --- a/plugins.c +++ b/plugins.c @@ -179,12 +179,14 @@ static DB_functions_t deadbeef_api = { .pl_append_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_append_meta, .pl_set_meta_int = (void (*) (DB_playItem_t *it, const char *key, int value))pl_set_meta_int, .pl_set_meta_float = (void (*) (DB_playItem_t *it, const char *key, float value))pl_set_meta_float, + .pl_delete_meta = (void (*)(DB_playItem_t *, const char *key))pl_delete_meta, .pl_find_meta = (const char *(*) (DB_playItem_t *, const char *))pl_find_meta, .pl_find_meta_int = (int (*) (DB_playItem_t *it, const char *key, int def))pl_find_meta_int, .pl_find_meta_float = (float (*) (DB_playItem_t *it, const char *key, float def))pl_find_meta_float, .pl_replace_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_replace_meta, .pl_delete_all_meta = (void (*) (DB_playItem_t *it))pl_delete_all_meta, - .pl_get_metadata = (DB_metaInfo_t *(*)(DB_playItem_t *it))pl_get_metadata, + .pl_get_metadata_head = (DB_metaInfo_t *(*)(DB_playItem_t *it))pl_get_metadata_head, + .pl_delete_metadata = (void (*)(DB_playItem_t *, DB_metaInfo_t *))pl_delete_metadata, // cuesheet support .pl_insert_cue_from_buffer = (DB_playItem_t *(*) (DB_playItem_t *after, DB_playItem_t *origin, const uint8_t *buffer, int buffersize, int numsamples, int samplerate))pl_insert_cue_from_buffer, .pl_insert_cue = (DB_playItem_t *(*)(DB_playItem_t *after, DB_playItem_t *origin, int numsamples, int samplerate))pl_insert_cue, diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 889e4984..66849dcc 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -61,7 +61,7 @@ build_key_list (const char ***pkeys, int props) { int n = 0; for (int i = 0; i < numtracks; i++) { - DB_metaInfo_t *meta = deadbeef->pl_get_metadata (tracks[i]); + DB_metaInfo_t *meta = deadbeef->pl_get_metadata_head (tracks[i]); while (meta) { if ((props && meta->key[0] == ':') || (!props && meta->key[0] != ':')) { int k = 0; @@ -471,34 +471,51 @@ show_track_properties_dlg (DB_playItem_t *it) { gtk_window_present (GTK_WINDOW (widget)); } -static gboolean -set_metadata_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { - GValue mult = {0,}; - gtk_tree_model_get_value (model, iter, 3, &mult); - int smult = g_value_get_int (&mult); - if (!smult) { - GValue key = {0,}, value = {0,}; - gtk_tree_model_get_value (model, iter, 2, &key); - gtk_tree_model_get_value (model, iter, 1, &value); - const char *skey = g_value_get_string (&key); - const char *svalue = g_value_get_string (&value); - - for (int i = 0; i < numtracks; i++) { - deadbeef->pl_replace_meta (tracks[i], skey, svalue); - } - } - - return FALSE; -} - void on_write_tags_clicked (GtkButton *button, gpointer user_data) { + deadbeef->pl_lock (); // put all metainfo into track GtkTreeView *tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); GtkTreeModel *model = GTK_TREE_MODEL (gtk_tree_view_get_model (tree)); - gtk_tree_model_foreach (model, set_metadata_cb, NULL); + for (int i = 0; i < numtracks; i++) { + DB_metaInfo_t *meta = deadbeef->pl_get_metadata_head (tracks[i]); + while (meta) { + DB_metaInfo_t *next = meta->next; + if (meta->key[0] != ':') { + GtkTreeIter iter; + gboolean res = gtk_tree_model_get_iter_first (model, &iter); + int mult = 0; + while (res) { + GValue key = {0,}; + gtk_tree_model_get_value (model, &iter, 2, &key); + const char *skey = g_value_get_string (&key); + + if (!strcmp (skey, meta->key)) { + GValue multvalue = {0,}; + gtk_tree_model_get_value (model, &iter, 3, &multvalue); + mult = g_value_get_int (&multvalue); + // keep field if multiple values are set, replace otherwise + if (!mult) { + GValue value = {0,}; + gtk_tree_model_get_value (model, &iter, 1, &value); + const char *svalue = g_value_get_string (&value); + deadbeef->pl_replace_meta (tracks[i], meta->key, svalue); + } + break; + } + res = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + } + if (!res) { + // delete this field (not in list) + deadbeef->pl_delete_metadata (tracks[i], meta); + } + } + meta = next; + } + } + for (int t = 0; t < numtracks; t++) { DB_playItem_t *track = tracks[t]; const char *decoder_id = deadbeef->pl_find_meta (track, ":DECODER"); @@ -517,6 +534,7 @@ on_write_tags_clicked (GtkButton *button, } } } + deadbeef->pl_unlock (); main_refresh (); search_refresh (); trkproperties_modified = 0; @@ -531,22 +549,50 @@ on_add_field_activate (GtkMenuItem *menuitem, GtkWidget *e; e = lookup_widget (dlg, "title_label"); gtk_label_set_text (GTK_LABEL(e), _("Name:")); - int res = gtk_dialog_run (GTK_DIALOG (dlg)); - if (res == GTK_RESPONSE_OK) { - e = lookup_widget (dlg, "title"); - const char *text = gtk_entry_get_text (GTK_ENTRY(e)); - - int l = strlen (text); - char title[l+3]; - snprintf (title, sizeof (title), "<%s>", text); - const char *value = ""; - const char *key = text; - - GtkTreeIter iter; - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, 0, title, 1, value, 2, key, -1); - trkproperties_modified = 1; + for (;;) { + int res = gtk_dialog_run (GTK_DIALOG (dlg)); + if (res == GTK_RESPONSE_OK) { + e = lookup_widget (dlg, "title"); + + const char *text = gtk_entry_get_text (GTK_ENTRY(e)); + + GtkTreeIter iter; + + // check if a field with the same name already exists + int dup = 0; + gboolean res = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + while (res) { + GValue value = {0,}; + gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 2, &value); + const char *svalue = g_value_get_string (&value); + if (!strcmp (svalue, text)) { + dup = 1; + break; + } + res = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + } + if (!dup) { + int l = strlen (text); + char title[l+3]; + snprintf (title, sizeof (title), "<%s>", text); + const char *value = ""; + const char *key = text; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, title, 1, value, 2, key, -1); + trkproperties_modified = 1; + } + else { + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (trackproperties), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Field with such name already exists, please try different name.")); + gtk_window_set_title (GTK_WINDOW (dlg), _("Cannot add field")); + + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + continue; + } + } + break; } gtk_widget_destroy (dlg); } @@ -563,7 +609,7 @@ on_remove_field_activate (GtkMenuItem *menuitem, return; } - GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (mainwin), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Really remove selected field?")); + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (trackproperties), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Really remove selected field?")); gtk_window_set_title (GTK_WINDOW (dlg), _("Warning")); int response = gtk_dialog_run (GTK_DIALOG (dlg)); -- cgit v1.2.3