summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deadbeef.h9
-rw-r--r--junklib.c51
-rw-r--r--playlist.c50
-rw-r--r--playlist.h8
-rw-r--r--plugins.c4
-rw-r--r--plugins/gtkui/trkproperties.c122
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
@@ -233,6 +233,9 @@ 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);
// returns index of 1st deleted item
@@ -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));