2 * Copyright (C) 2003-2009 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 #define G_LOG_DOMAIN "id3"
37 # ifndef ID3_FRAME_COMPOSER
38 # define ID3_FRAME_COMPOSER "TCOM"
40 # ifndef ID3_FRAME_DISC
41 # define ID3_FRAME_DISC "TPOS"
44 #ifndef ID3_FRAME_ARTIST_SORT
45 #define ID3_FRAME_ARTIST_SORT "TSOP"
48 #ifndef ID3_FRAME_ALBUM_ARTIST_SORT
49 #define ID3_FRAME_ALBUM_ARTIST_SORT "TSO2" /* this one is unofficial, introduced by Itunes */
52 #ifndef ID3_FRAME_ALBUM_ARTIST
53 #define ID3_FRAME_ALBUM_ARTIST "TPE2"
57 tag_is_id3v1(struct id3_tag
*tag
)
59 return (id3_tag_options(tag
, 0, 0) & ID3_TAG_OPTION_ID3V1
) != 0;
63 tag_id3_getstring(const struct id3_frame
*frame
, unsigned i
)
65 union id3_field
*field
;
66 const id3_ucs4_t
*ucs4
;
68 field
= id3_frame_field(frame
, i
);
72 ucs4
= id3_field_getstring(field
);
76 return id3_ucs4_utf8duplicate(ucs4
);
79 /* This will try to convert a string to utf-8,
82 import_id3_string(bool is_id3v1
, const id3_ucs4_t
*ucs4
)
84 id3_utf8_t
*utf8
, *utf8_stripped
;
88 /* use encoding field here? */
90 (encoding
= config_get_string(CONF_ID3V1_ENCODING
, NULL
)) != NULL
) {
91 isostr
= id3_ucs4_latin1duplicate(ucs4
);
92 if (G_UNLIKELY(!isostr
)) {
97 g_convert_with_fallback((const char*)isostr
, -1,
99 NULL
, NULL
, NULL
, NULL
);
101 g_debug("Unable to convert %s string to UTF-8: '%s'",
108 utf8
= id3_ucs4_utf8duplicate(ucs4
);
109 if (G_UNLIKELY(!utf8
)) {
114 utf8_stripped
= (id3_utf8_t
*)g_strdup(g_strstrip((gchar
*)utf8
));
117 return utf8_stripped
;
121 * Import a "Text information frame" (ID3v2.4.0 section 4.2). It
128 tag_id3_import_text(struct tag
*dest
, struct id3_tag
*tag
, const char *id
,
131 struct id3_frame
const *frame
;
132 id3_ucs4_t
const *ucs4
;
134 union id3_field
const *field
;
135 unsigned int nstrings
, i
;
137 frame
= id3_tag_findframe(tag
, id
, 0);
138 if (frame
== NULL
|| frame
->nfields
!= 2)
141 /* check the encoding field */
143 field
= id3_frame_field(frame
, 0);
144 if (field
== NULL
|| field
->type
!= ID3_FIELD_TYPE_TEXTENCODING
)
147 /* process the value(s) */
149 field
= id3_frame_field(frame
, 1);
150 if (field
== NULL
|| field
->type
!= ID3_FIELD_TYPE_STRINGLIST
)
153 /* Get the number of strings available */
154 nstrings
= id3_field_getnstrings(field
);
155 for (i
= 0; i
< nstrings
; i
++) {
156 ucs4
= id3_field_getstrings(field
, i
);
160 if (type
== TAG_ITEM_GENRE
)
161 ucs4
= id3_genre_name(ucs4
);
163 utf8
= import_id3_string(tag_is_id3v1(tag
), ucs4
);
167 tag_add_item(dest
, type
, (char *)utf8
);
173 * Import a "Comment frame" (ID3v2.4.0 section 4.10). It
179 * - full string (we use this one)
182 tag_id3_import_comment(struct tag
*dest
, struct id3_tag
*tag
, const char *id
,
185 struct id3_frame
const *frame
;
186 id3_ucs4_t
const *ucs4
;
188 union id3_field
const *field
;
190 frame
= id3_tag_findframe(tag
, id
, 0);
191 if (frame
== NULL
|| frame
->nfields
!= 4)
194 /* for now I only read the 4th field, with the fullstring */
195 field
= id3_frame_field(frame
, 3);
199 ucs4
= id3_field_getfullstring(field
);
203 utf8
= import_id3_string(tag_is_id3v1(tag
), ucs4
);
207 tag_add_item(dest
, type
, (char *)utf8
);
212 * Parse a TXXX name, and convert it to a tag_type enum value.
213 * Returns TAG_NUM_OF_ITEM_TYPES if the TXXX name is not understood.
216 tag_id3_parse_txxx_name(const char *name
)
218 static const struct {
221 } musicbrainz_txxx
[] = {
222 { TAG_ALBUM_ARTIST_SORT
, "ALBUMARTISTSORT" },
223 { TAG_MUSICBRAINZ_ARTISTID
, "MusicBrainz Artist Id" },
224 { TAG_MUSICBRAINZ_ALBUMID
, "MusicBrainz Album Id" },
225 { TAG_MUSICBRAINZ_ALBUMARTISTID
,
226 "MusicBrainz Album Artist Id" },
227 { TAG_MUSICBRAINZ_TRACKID
, "MusicBrainz Track Id" },
230 for (unsigned i
= 0; i
< G_N_ELEMENTS(musicbrainz_txxx
); ++i
)
231 if (strcmp(name
, musicbrainz_txxx
[i
].name
) == 0)
232 return musicbrainz_txxx
[i
].type
;
234 return TAG_NUM_OF_ITEM_TYPES
;
238 * Import all known MusicBrainz tags from TXXX frames.
241 tag_id3_import_musicbrainz(struct tag
*mpd_tag
, struct id3_tag
*id3_tag
)
243 for (unsigned i
= 0;; ++i
) {
244 const struct id3_frame
*frame
;
245 id3_utf8_t
*name
, *value
;
248 frame
= id3_tag_findframe(id3_tag
, "TXXX", i
);
252 name
= tag_id3_getstring(frame
, 1);
256 type
= tag_id3_parse_txxx_name((const char*)name
);
259 if (type
== TAG_NUM_OF_ITEM_TYPES
)
262 value
= tag_id3_getstring(frame
, 2);
266 tag_add_item(mpd_tag
, type
, (const char*)value
);
272 * Imports the MusicBrainz TrackId from the UFID tag.
275 tag_id3_import_ufid(struct tag
*mpd_tag
, struct id3_tag
*id3_tag
)
277 for (unsigned i
= 0;; ++i
) {
278 const struct id3_frame
*frame
;
279 union id3_field
*field
;
280 const id3_latin1_t
*name
;
281 const id3_byte_t
*value
;
284 frame
= id3_tag_findframe(id3_tag
, "UFID", i
);
288 field
= id3_frame_field(frame
, 0);
292 name
= id3_field_getlatin1(field
);
294 strcmp((const char *)name
, "http://musicbrainz.org") != 0)
297 field
= id3_frame_field(frame
, 1);
301 value
= id3_field_getbinarydata(field
, &length
);
302 if (value
== NULL
|| length
== 0)
305 tag_add_item_n(mpd_tag
, TAG_MUSICBRAINZ_TRACKID
,
306 (const char*)value
, length
);
310 struct tag
*tag_id3_import(struct id3_tag
* tag
)
312 struct tag
*ret
= tag_new();
314 tag_id3_import_text(ret
, tag
, ID3_FRAME_ARTIST
, TAG_ITEM_ARTIST
);
315 tag_id3_import_text(ret
, tag
, ID3_FRAME_ALBUM_ARTIST
,
316 TAG_ITEM_ALBUM_ARTIST
);
317 tag_id3_import_text(ret
, tag
, ID3_FRAME_ARTIST_SORT
,
319 tag_id3_import_text(ret
, tag
, ID3_FRAME_ALBUM_ARTIST_SORT
,
320 TAG_ALBUM_ARTIST_SORT
);
321 tag_id3_import_text(ret
, tag
, ID3_FRAME_TITLE
, TAG_ITEM_TITLE
);
322 tag_id3_import_text(ret
, tag
, ID3_FRAME_ALBUM
, TAG_ITEM_ALBUM
);
323 tag_id3_import_text(ret
, tag
, ID3_FRAME_TRACK
, TAG_ITEM_TRACK
);
324 tag_id3_import_text(ret
, tag
, ID3_FRAME_YEAR
, TAG_ITEM_DATE
);
325 tag_id3_import_text(ret
, tag
, ID3_FRAME_GENRE
, TAG_ITEM_GENRE
);
326 tag_id3_import_text(ret
, tag
, ID3_FRAME_COMPOSER
, TAG_ITEM_COMPOSER
);
327 tag_id3_import_text(ret
, tag
, "TPE3", TAG_ITEM_PERFORMER
);
328 tag_id3_import_text(ret
, tag
, "TPE4", TAG_ITEM_PERFORMER
);
329 tag_id3_import_comment(ret
, tag
, ID3_FRAME_COMMENT
, TAG_ITEM_COMMENT
);
330 tag_id3_import_text(ret
, tag
, ID3_FRAME_DISC
, TAG_ITEM_DISC
);
332 tag_id3_import_musicbrainz(ret
, tag
);
333 tag_id3_import_ufid(ret
, tag
);
335 if (tag_is_empty(ret
)) {
344 fill_buffer(void *buf
, size_t size
, FILE *stream
, long offset
, int whence
)
346 if (fseek(stream
, offset
, whence
) != 0) return 0;
347 return fread(buf
, 1, size
, stream
);
351 get_id3v2_footer_size(FILE *stream
, long offset
, int whence
)
353 id3_byte_t buf
[ID3_TAG_QUERYSIZE
];
356 bufsize
= fill_buffer(buf
, ID3_TAG_QUERYSIZE
, stream
, offset
, whence
);
357 if (bufsize
<= 0) return 0;
358 return id3_tag_query(buf
, bufsize
);
361 static struct id3_tag
*
362 tag_id3_read(FILE *stream
, long offset
, int whence
)
365 id3_byte_t query_buffer
[ID3_TAG_QUERYSIZE
];
366 id3_byte_t
*tag_buffer
;
368 int query_buffer_size
;
371 /* It's ok if we get less than we asked for */
372 query_buffer_size
= fill_buffer(query_buffer
, ID3_TAG_QUERYSIZE
,
373 stream
, offset
, whence
);
374 if (query_buffer_size
<= 0) return NULL
;
376 /* Look for a tag header */
377 tag_size
= id3_tag_query(query_buffer
, query_buffer_size
);
378 if (tag_size
<= 0) return NULL
;
380 /* Found a tag. Allocate a buffer and read it in. */
381 tag_buffer
= g_malloc(tag_size
);
382 if (!tag_buffer
) return NULL
;
384 tag_buffer_size
= fill_buffer(tag_buffer
, tag_size
, stream
, offset
, whence
);
385 if (tag_buffer_size
< tag_size
) {
390 tag
= id3_tag_parse(tag_buffer
, tag_buffer_size
);
397 static struct id3_tag
*
398 tag_id3_find_from_beginning(FILE *stream
)
401 struct id3_tag
*seektag
;
402 struct id3_frame
*frame
;
405 tag
= tag_id3_read(stream
, 0, SEEK_SET
);
408 } else if (tag_is_id3v1(tag
)) {
409 /* id3v1 tags don't belong here */
414 /* We have an id3v2 tag, so let's look for SEEK frames */
415 while ((frame
= id3_tag_findframe(tag
, "SEEK", 0))) {
416 /* Found a SEEK frame, get it's value */
417 seek
= id3_field_getint(id3_frame_field(frame
, 0));
421 /* Get the tag specified by the SEEK frame */
422 seektag
= tag_id3_read(stream
, seek
, SEEK_CUR
);
423 if (!seektag
|| tag_is_id3v1(seektag
))
426 /* Replace the old tag with the new one */
434 static struct id3_tag
*
435 tag_id3_find_from_end(FILE *stream
)
438 struct id3_tag
*v1tag
;
441 /* Get an id3v1 tag from the end of file for later use */
442 v1tag
= tag_id3_read(stream
, -128, SEEK_END
);
444 /* Get the id3v2 tag size from the footer (located before v1tag) */
445 tagsize
= get_id3v2_footer_size(stream
, (v1tag
? -128 : 0) - 10, SEEK_END
);
449 /* Get the tag which the footer belongs to */
450 tag
= tag_id3_read(stream
, tagsize
, SEEK_CUR
);
454 /* We have an id3v2 tag, so ditch v1tag */
455 id3_tag_delete(v1tag
);
460 static struct id3_tag
*
461 tag_id3_riff_aiff_load(FILE *file
)
468 size
= riff_seek_id3(file
);
470 size
= aiff_seek_id3(file
);
474 if (size
> 256 * 1024)
475 /* too large, don't allocate so much memory */
478 buffer
= g_malloc(size
);
479 ret
= fread(buffer
, size
, 1, file
);
481 g_warning("Failed to read RIFF chunk");
486 tag
= id3_tag_parse(buffer
, size
);
491 struct tag
*tag_id3_load(const char *file
)
497 stream
= fopen(file
, "r");
499 g_debug("tag_id3_load: Failed to open file: '%s', %s",
500 file
, strerror(errno
));
504 tag
= tag_id3_find_from_beginning(stream
);
506 tag
= tag_id3_riff_aiff_load(stream
);
508 tag
= tag_id3_find_from_end(stream
);
514 ret
= tag_id3_import(tag
);