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