1 /*
2   This file is part of Deadbeef Player source code
3   http://deadbeef.sourceforge.net
4 
5   track metadata management
6 
7   Copyright (C) 2009-2013 Alexey Yakovenko
8 
9   This software is provided 'as-is', without any express or implied
10   warranty.  In no event will the authors be held liable for any damages
11   arising from the use of this software.
12 
13   Permission is granted to anyone to use this software for any purpose,
14   including commercial applications, and to alter it and redistribute it
15   freely, subject to the following restrictions:
16 
17   1. The origin of this software must not be misrepresented; you must not
18      claim that you wrote the original software. If you use this software
19      in a product, an acknowledgment in the product documentation would be
20      appreciated but is not required.
21   2. Altered source versions must be plainly marked as such, and must not be
22      misrepresented as being the original software.
23   3. This notice may not be removed or altered from any source distribution.
24 
25   Alexey Yakovenko waker@users.sourceforge.net
26 */
27 
28 #include <string.h>
29 #include <stdlib.h>
30 #include "playlist.h"
31 #include "deadbeef.h"
32 #include "metacache.h"
33 
34 #define LOCK {pl_lock();}
35 #define UNLOCK {pl_unlock();}
36 
37 void
pl_add_meta(playItem_t * it,const char * key,const char * value)38 pl_add_meta (playItem_t *it, const char *key, const char *value) {
39     if (!value || !*value) {
40         return;
41     }
42     LOCK;
43     // check if it's already set
44     DB_metaInfo_t *normaltail = NULL;
45     DB_metaInfo_t *propstart = NULL;
46     DB_metaInfo_t *tail = NULL;
47     DB_metaInfo_t *m = it->meta;
48     while (m) {
49         if (!strcasecmp (key, m->key)) {
50             // duplicate key
51             UNLOCK;
52             return;
53         }
54         // find end of normal metadata
55         if (!normaltail && (!m->next || m->key[0] == ':' || m->key[0] == '_' || m->key[0] == '!')) {
56             normaltail = tail;
57             propstart = m;
58             if (key[0] != ':' && key[0] != '_' && key[0] != '!') {
59                 break;
60             }
61         }
62         // find end of properties
63         tail = m;
64         m = m->next;
65     }
66     // add
67     m = malloc (sizeof (DB_metaInfo_t));
68     memset (m, 0, sizeof (DB_metaInfo_t));
69     m->key = metacache_add_string (key);
70     m->value = metacache_add_string (value);
71 
72     if (key[0] == ':' || key[0] == '_' || key[0] == '!') {
73         if (tail) {
74             tail->next = m;
75         }
76         else {
77             it->meta = m;
78         }
79     }
80     else {
81         m->next = propstart;
82         if (normaltail) {
83             normaltail->next = m;
84         }
85         else {
86             it->meta = m;
87         }
88     }
89     UNLOCK;
90 }
91 
92 void
pl_append_meta(playItem_t * it,const char * key,const char * value)93 pl_append_meta (playItem_t *it, const char *key, const char *value) {
94     pl_lock ();
95     const char *old = pl_find_meta_raw (it, key);
96 
97     if (old && (!strcasecmp (key, "cuesheet") || !strcasecmp (key, "log"))) {
98         pl_unlock ();
99         return;
100     }
101 
102     size_t newlen = strlen (value);
103     if (!old) {
104         pl_add_meta (it, key, value);
105     }
106     else {
107         // check for duplicate data
108         const char *str = old;
109         size_t len;
110         while (str) {
111             char *next = strchr (str, '\n');
112 
113             if (next) {
114                 len = next - str;
115                 next++;
116             }
117             else {
118                 len = strlen (str);
119             }
120 
121             if (len == newlen && !memcmp (str, value, len)) {
122                 pl_unlock ();
123                 return;
124             }
125 
126             str = next;
127         }
128         size_t sz = strlen (old) + newlen + 2;
129         char out[sz];
130         snprintf (out, sz, "%s\n%s", old, value);
131         pl_replace_meta (it, key, out);
132     }
133     pl_unlock ();
134 }
135 
136 void
pl_replace_meta(playItem_t * it,const char * key,const char * value)137 pl_replace_meta (playItem_t *it, const char *key, const char *value) {
138     LOCK;
139     // check if it's already set
140     DB_metaInfo_t *m = it->meta;
141     while (m) {
142         if (!strcasecmp (key, m->key)) {
143             break;
144         }
145         m = m->next;
146     }
147     if (m) {
148         metacache_remove_string (m->value);
149         m->value = metacache_add_string (value);
150         UNLOCK;
151         return;
152     }
153     else {
154         pl_add_meta (it, key, value);
155     }
156     UNLOCK;
157 }
158 
159 void
pl_set_meta_int(playItem_t * it,const char * key,int value)160 pl_set_meta_int (playItem_t *it, const char *key, int value) {
161     char s[20];
162     snprintf (s, sizeof (s), "%d", value);
163     pl_replace_meta (it, key, s);
164 }
165 
166 void
pl_set_meta_float(playItem_t * it,const char * key,float value)167 pl_set_meta_float (playItem_t *it, const char *key, float value) {
168     char s[20];
169     snprintf (s, sizeof (s), "%f", value);
170     pl_replace_meta (it, key, s);
171 }
172 
173 void
pl_delete_meta(playItem_t * it,const char * key)174 pl_delete_meta (playItem_t *it, const char *key) {
175     pl_lock ();
176     DB_metaInfo_t *prev = NULL;
177     DB_metaInfo_t *m = it->meta;
178     while (m) {
179         if (!strcasecmp (key, m->key)) {
180             if (prev) {
181                 prev->next = m->next;
182             }
183             else {
184                 it->meta = m->next;
185             }
186             metacache_remove_string (m->key);
187             metacache_remove_string (m->value);
188             free (m);
189             break;
190         }
191         prev = m;
192         m = m->next;
193     }
194     pl_unlock ();
195 }
196 
197 const char *
pl_find_meta(playItem_t * it,const char * key)198 pl_find_meta (playItem_t *it, const char *key) {
199     pl_ensure_lock ();
200     DB_metaInfo_t *m = it->meta;
201 
202     if (key && key[0] == ':') {
203         // try to find an override
204         while (m) {
205             if (m->key[0] == '!' && !strcasecmp (key+1, m->key+1)) {
206                 return m->value;
207             }
208             m = m->next;
209         }
210     }
211 
212     m = it->meta;
213     while (m) {
214         if (!strcasecmp (key, m->key)) {
215             return m->value;
216         }
217         m = m->next;
218     }
219     return NULL;
220 }
221 
222 const char *
pl_find_meta_raw(playItem_t * it,const char * key)223 pl_find_meta_raw (playItem_t *it, const char *key) {
224     pl_ensure_lock ();
225     DB_metaInfo_t *m = it->meta;
226     while (m) {
227         if (!strcasecmp (key, m->key)) {
228             return m->value;
229         }
230         m = m->next;
231     }
232     return NULL;
233 }
234 
235 int
pl_find_meta_int(playItem_t * it,const char * key,int def)236 pl_find_meta_int (playItem_t *it, const char *key, int def) {
237     pl_lock ();
238     const char *val = pl_find_meta (it, key);
239     int res = val ? atoi (val) : def;
240     pl_unlock ();
241     return res;
242 }
243 
244 float
pl_find_meta_float(playItem_t * it,const char * key,float def)245 pl_find_meta_float (playItem_t *it, const char *key, float def) {
246     pl_lock ();
247     const char *val = pl_find_meta (it, key);
248     float res = val ? atof (val) : def;
249     pl_unlock ();
250     return res;
251 }
252 
253 DB_metaInfo_t *
pl_get_metadata_head(playItem_t * it)254 pl_get_metadata_head (playItem_t *it) {
255     return it->meta;
256 }
257 
258 void
pl_delete_metadata(playItem_t * it,DB_metaInfo_t * meta)259 pl_delete_metadata (playItem_t *it, DB_metaInfo_t *meta) {
260     pl_lock ();
261     DB_metaInfo_t *prev = NULL;
262     DB_metaInfo_t *m = it->meta;
263     while (m) {
264         if (m == meta) {
265             if (prev) {
266                 prev->next = m->next;
267             }
268             else {
269                 it->meta = m->next;
270             }
271             metacache_remove_string (m->key);
272             metacache_remove_string (m->value);
273             free (m);
274             break;
275         }
276         prev = m;
277         m = m->next;
278     }
279     pl_unlock ();
280 }
281 
282 void
pl_delete_all_meta(playItem_t * it)283 pl_delete_all_meta (playItem_t *it) {
284     LOCK;
285     DB_metaInfo_t *m = it->meta;
286     DB_metaInfo_t *prev = NULL;
287     while (m) {
288         DB_metaInfo_t *next = m->next;
289         if (m->key[0] == ':'  || m->key[0] == '_' || m->key[0] == '!') {
290             prev = m;
291         }
292         else {
293             if (prev) {
294                 prev->next = next;
295             }
296             else {
297                 it->meta = next;
298             }
299             metacache_remove_string (m->key);
300             metacache_remove_string (m->value);
301             free (m);
302         }
303         m = next;
304     }
305     uint32_t f = pl_get_item_flags (it);
306     f &= ~DDB_TAG_MASK;
307     pl_set_item_flags (it, f);
308     UNLOCK;
309 }
310 
311 int
pl_get_meta(playItem_t * it,const char * key,char * val,int size)312 pl_get_meta (playItem_t *it, const char *key, char *val, int size) {
313     *val = 0;
314     pl_lock ();
315     const char *v = pl_find_meta (it, key);
316     if (!v) {
317         pl_unlock ();
318         return 0;
319     }
320     strncpy (val, v, size);
321     pl_unlock ();
322     return 1;
323 }
324 
325 int
pl_get_meta_raw(playItem_t * it,const char * key,char * val,int size)326 pl_get_meta_raw (playItem_t *it, const char *key, char *val, int size) {
327     *val = 0;
328     pl_lock ();
329     const char *v = pl_find_meta_raw (it, key);
330     if (!v) {
331         pl_unlock ();
332         return 0;
333     }
334     strncpy (val, v, size);
335     pl_unlock ();
336     return 1;
337 }
338 
339 int
pl_meta_exists(playItem_t * it,const char * key)340 pl_meta_exists (playItem_t *it, const char *key) {
341     pl_lock ();
342     const char *v = pl_find_meta (it, key);
343     pl_unlock ();
344     return v ? 1 : 0;
345 }
346