1 /*                                                     -*- linux-c -*-
2     Copyright (C) 2007 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18     $Id: metadata_api.c 1237 2012-02-04 10:30:17Z assworth $
19 */
20 
21 #include <config.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "i18n.h"
28 #include "options.h"
29 #include "decoder/file_decoder.h"
30 #include "utils.h"
31 #include "metadata.h"
32 #include "volume.h"
33 #include "metadata_api.h"
34 
35 
36 extern options_t options;
37 
38 /* get frame of given type, with preference between different tags.
39    preference order: MPEGSTREAM > GEN_STREAM > OXC > APE > ID3v2 > ID3v1 */
40 meta_frame_t *
metadata_pref_frame_by_type(metadata_t * meta,int type,meta_frame_t * root)41 metadata_pref_frame_by_type(metadata_t * meta, int type, meta_frame_t * root) {
42 
43 	meta_frame_t * frame;
44 	static int tag;
45 
46 	if (root == NULL) {
47 		tag = META_TAG_MAX;
48 	}
49 
50 	while (tag > 0) {
51 		frame = metadata_get_frame_by_tag_and_type(meta, tag, type, root);
52 		if (frame) {
53 			return frame;
54 		}
55 		tag >>= 1;
56 	}
57 
58 	return NULL;
59 }
60 
61 
62 int
metadata_get_string_field(metadata_t * meta,int type,char ** str)63 metadata_get_string_field(metadata_t * meta, int type, char ** str) {
64 
65 	meta_frame_t * frame;
66 
67 	if (meta == NULL)
68 		return 0;
69 	frame = metadata_pref_frame_by_type(meta, type, NULL);
70 	if (frame != NULL) {
71 		*str = frame->field_val;
72 		return 1;
73 	} else {
74 		return 0;
75 	}
76 
77 }
78 
79 int
metadata_get_title(metadata_t * meta,char ** str)80 metadata_get_title(metadata_t * meta, char ** str) {
81 
82 #ifdef HAVE_MOD
83 	meta_frame_t * frame;
84 #endif /* HAVE_MOD */
85 
86 	if (meta == NULL)
87 		return 0;
88 
89 #ifdef HAVE_MOD
90 	frame = metadata_get_frame_by_tag_and_type(meta, META_TAG_MODINFO, META_FIELD_MODINFO, NULL);
91 	if (frame != NULL) {
92 		mod_info * mi = (mod_info *)frame->data;
93 		*str = mi->title;
94 		return 1;
95 	}
96 #endif /* HAVE_MOD */
97 
98 	return metadata_get_string_field(meta, META_FIELD_TITLE, str);
99 }
100 
101 
102 int
metadata_get_artist(metadata_t * meta,char ** str)103 metadata_get_artist(metadata_t * meta, char ** str) {
104 	return metadata_get_string_field(meta, META_FIELD_ARTIST, str);
105 }
106 
107 
108 int
metadata_get_album(metadata_t * meta,char ** str)109 metadata_get_album(metadata_t * meta, char ** str) {
110 	return metadata_get_string_field(meta, META_FIELD_ALBUM, str);
111 }
112 
113 
114 int
metadata_get_date(metadata_t * meta,char ** str)115 metadata_get_date(metadata_t * meta, char ** str) {
116 	return metadata_get_string_field(meta, META_FIELD_DATE, str);
117 }
118 
119 
120 int
metadata_get_genre(metadata_t * meta,char ** str)121 metadata_get_genre(metadata_t * meta, char ** str) {
122 	return metadata_get_string_field(meta, META_FIELD_GENRE, str);
123 }
124 
125 
126 int
metadata_get_comment(metadata_t * meta,char ** str)127 metadata_get_comment(metadata_t * meta, char ** str) {
128 	return metadata_get_string_field(meta, META_FIELD_COMMENT, str);
129 }
130 
131 
132 int
metadata_get_icy_name(metadata_t * meta,char ** str)133 metadata_get_icy_name(metadata_t * meta, char ** str) {
134 	return metadata_get_string_field(meta, META_FIELD_ICY_NAME, str);
135 }
136 
137 
138 int
metadata_get_icy_descr(metadata_t * meta,char ** str)139 metadata_get_icy_descr(metadata_t * meta, char ** str) {
140 	return metadata_get_string_field(meta, META_FIELD_ICY_DESCR, str);
141 }
142 
143 
144 int
metadata_get_tracknum(metadata_t * meta,int * val)145 metadata_get_tracknum(metadata_t * meta, int * val) {
146 
147 	meta_frame_t * frame;
148 
149 	if (meta == NULL)
150 		return 0;
151 	frame = metadata_pref_frame_by_type(meta, META_FIELD_TRACKNO, NULL);
152 	if (frame != NULL) {
153 		*val = frame->int_val;
154 		return 1;
155 	} else {
156 		return 0;
157 	}
158 }
159 
160 
161 int
metadata_get_rva(metadata_t * meta,float * fval)162 metadata_get_rva(metadata_t * meta, float * fval) {
163 
164 	int rva_type;
165 	meta_frame_t * frame;
166 
167 	if (meta == NULL)
168 		return 0;
169 
170 	switch (options.replaygain_tag_to_use) {
171 	case 0: rva_type = META_FIELD_RG_TRACK_GAIN;
172 		break;
173 	default: rva_type = META_FIELD_RG_ALBUM_GAIN;
174 		break;
175 	}
176 
177 	frame = metadata_pref_frame_by_type(meta, rva_type, NULL);
178 	if (frame != NULL) {
179 		*fval = rva_from_replaygain(frame->float_val);
180 		return 1;
181 	} else {
182 		/* fallback on the other ReplayGain frame */
183 		if (rva_type == META_FIELD_RG_TRACK_GAIN) {
184 			rva_type = META_FIELD_RG_ALBUM_GAIN;
185 		} else {
186 			rva_type = META_FIELD_RG_TRACK_GAIN;
187 		}
188 		frame = metadata_pref_frame_by_type(meta, rva_type, NULL);
189 		if (frame != NULL) {
190 			*fval = rva_from_replaygain(frame->float_val);
191 			return 1;
192 		} else {
193 			/* fallback on RVA frame */
194 			frame = metadata_pref_frame_by_type(meta, META_FIELD_RVA2, NULL);
195 			if (frame != NULL) {
196 				*fval = frame->float_val;
197 				return 1;
198 			}
199 		}
200 	}
201 	return 0;
202 }
203 
204 
205 void
meta_update_frame_data(meta_frame_t * frame,char * str,int val,float fval)206 meta_update_frame_data(meta_frame_t * frame, char * str, int val, float fval) {
207 
208 	if (META_FIELD_TEXT(frame->type)) {
209 		if (frame->field_val != NULL) {
210 			free(frame->field_val);
211 		}
212 		frame->field_val = strdup(str);
213 	} else if (META_FIELD_INT(frame->type)) {
214 		frame->int_val = val;
215 	} else if (META_FIELD_FLOAT(frame->type)) {
216 		frame->float_val = fval;
217 	} else { /* no binary frames in update_basic mode */
218 		fprintf(stderr, "meta_update_frame_data: programmer error\n");
219 	}
220 }
221 
222 
223 void
meta_update_frame(metadata_t * meta,int add_tags,int type,char * str,int val,float fval)224 meta_update_frame(metadata_t * meta, int add_tags, int type,
225 		  char * str, int val, float fval) {
226 
227 	int tag = 1;
228 	while (tag) {
229 		meta_frame_t * frame;
230 
231 		if ((meta->valid_tags & tag) == 0) {
232 			tag <<= 1;
233 			continue;
234 		}
235 
236 		frame = metadata_get_frame_by_tag_and_type(meta, tag, type, NULL);
237 		if ((frame == NULL) && ((add_tags & tag) != 0)) {
238 
239 			char * s;
240 			/* make sure frame type is available in this tag */
241 			if (!meta_get_fieldname_embedded(tag, type, &s)) {
242 				tag <<= 1;
243 				continue;
244 			}
245 
246 			/* add new frame */
247 			frame = meta_frame_new();
248 			if (frame == NULL) {
249 				fprintf(stderr, "meta_update_frame: calloc error\n");
250 				return;
251 			}
252 			frame->tag = tag;
253 			frame->type = type;
254 			frame->flags = meta_get_default_flags(tag, type);
255 			meta_update_frame_data(frame, str, val, fval);
256 			metadata_add_frame(meta, frame);
257 		} else if (frame != NULL) {
258 			meta_update_frame_data(frame, str, val, fval);
259 		}
260 		tag <<= 1;
261 	}
262 }
263 
264 
265 int
meta_update_basic(char * filename,char * title,char * artist,char * album,char * comment,char * genre,char * date,int trackno)266 meta_update_basic(char * filename,
267 		  char * title, char * artist, char * album,
268 		  char * comment, char * genre, char * date, int trackno) {
269 
270 	file_decoder_t * fdec = file_decoder_new();
271 	int add_tags;
272 	int ret;
273 
274 	if (fdec == NULL) {
275 		return META_ERROR_NOMEM;
276 	}
277 
278 	if (file_decoder_open(fdec, filename) != 0) {
279 		file_decoder_delete(fdec);
280 		return META_ERROR_OPEN;
281 	}
282 
283 	if (fdec->meta == NULL) {
284 		file_decoder_close(fdec);
285 		file_decoder_delete(fdec);
286 		return META_ERROR_NO_METASUPPORT;
287 	}
288 
289 	if (!fdec->meta->writable) {
290 		file_decoder_close(fdec);
291 		file_decoder_delete(fdec);
292 		return META_ERROR_NOT_WRITABLE;
293 	}
294 
295 	if (fdec->file_lib == MAD_LIB) {
296 		add_tags = 0;
297 		if (options.batch_mpeg_add_id3v1) {
298 			add_tags |= META_TAG_ID3v1;
299 		}
300 		if (options.batch_mpeg_add_id3v2) {
301 			add_tags |= META_TAG_ID3v2;
302 		}
303 		if (options.batch_mpeg_add_ape) {
304 			add_tags |= META_TAG_APE;
305 		}
306 	} else {
307 		add_tags = fdec->meta->valid_tags;
308 	}
309 
310 	if (fdec->meta_write == NULL) {
311 		file_decoder_close(fdec);
312 		file_decoder_delete(fdec);
313 		return META_ERROR_INTERNAL;
314 	}
315 
316 	if (title != NULL && !is_all_wspace(title)) {
317 		cut_trailing_whitespace(title);
318 		meta_update_frame(fdec->meta, add_tags, META_FIELD_TITLE, title, 0, 0.0f);
319 	}
320 	if (artist != NULL && !is_all_wspace(artist)) {
321 		cut_trailing_whitespace(artist);
322 		meta_update_frame(fdec->meta, add_tags, META_FIELD_ARTIST, artist, 0, 0.0f);
323 	}
324 	if (album != NULL && !is_all_wspace(album)) {
325 		cut_trailing_whitespace(album);
326 		meta_update_frame(fdec->meta, add_tags, META_FIELD_ALBUM, album, 0, 0.0f);
327 	}
328 	if (trackno != -1) {
329 		meta_update_frame(fdec->meta, add_tags, META_FIELD_TRACKNO, NULL, trackno, 0.0f);
330 	}
331 	if (date != NULL && !is_all_wspace(date)) {
332 		cut_trailing_whitespace(date);
333 		meta_update_frame(fdec->meta, add_tags, META_FIELD_DATE, date, 0, 0.0f);
334 	}
335 	if (genre != NULL && !is_all_wspace(genre)) {
336 		cut_trailing_whitespace(genre);
337 		meta_update_frame(fdec->meta, add_tags, META_FIELD_GENRE, genre, 0, 0.0f);
338 	}
339 	if (comment != NULL && !is_all_wspace(comment)) {
340 		cut_trailing_whitespace(comment);
341 		meta_update_frame(fdec->meta, add_tags, META_FIELD_COMMENT, comment, 0, 0.0f);
342 	}
343 
344 	ret = fdec->meta_write(fdec, fdec->meta);
345 	file_decoder_close(fdec);
346 	file_decoder_delete(fdec);
347 
348 	return ret;
349 }
350 
351 const char *
metadata_strerror(int error)352 metadata_strerror(int error) {
353 
354 	switch (error) {
355 	case META_ERROR_NONE: return _("Success");
356 	case META_ERROR_NOMEM: return _("Memory allocation error");
357 	case META_ERROR_OPEN: return _("Unable to open file");
358 	case META_ERROR_NO_METASUPPORT: return _("No metadata support for this format");
359 	case META_ERROR_NOT_WRITABLE: return _("File is not writable");
360 	case META_ERROR_INVALID_TRACKNO: return _("Invalid 'Track no.' field value");
361 	case META_ERROR_INVALID_GENRE: return _("Invalid 'Genre' field value");
362 	case META_ERROR_INVALID_CODING: return _("Conversion to target charset failed");
363 	case META_ERROR_INTERNAL: return _("Internal error");
364 	default: return _("Unknown error");
365 	}
366 }
367