const id3_ucs4_t* metadata_getstring(const struct id3_tag* tag, const char* id, enum id3_field_textencoding* encoding) { int nstrings, j; char const *name; id3_ucs4_t const *ucs4; union id3_field const *field; struct id3_frame const *frame; frame = id3_tag_findframe(tag, id, 0); if (frame == 0) return id3_ucs4_empty; *encoding = id3_field_gettextencoding(id3_frame_field(frame, 0)); field = id3_frame_field(frame, 1); if (field == 0) return id3_ucs4_empty; nstrings = id3_field_getnstrings(field); ucs4 = id3_ucs4_empty; for (j = 0; j < nstrings; ++j) { ucs4 = id3_field_getstrings(field, j); if (ucs4 && *ucs4) break; } return ucs4; }
const id3_ucs4_t* id3_metadata_getcomment(const struct id3_tag* tag, enum id3_field_textencoding* encoding) { union id3_field const *field; struct id3_frame const *frame; const id3_ucs4_t* ucs4 = 0; int commentNumber = 0; // return the first non-empty comment do { frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, commentNumber++); if (frame == 0) return id3_ucs4_empty; *encoding = id3_field_gettextencoding(id3_frame_field(frame, 0)); field = id3_frame_field(frame, 3); if (field == 0) return id3_ucs4_empty; ucs4 = id3_field_getfullstring(field); if (!ucs4) return id3_ucs4_empty; } while (*ucs4 == 0); return ucs4; }
id3_ucs4_list_t *metadata_getstrings(const struct id3_tag* tag, const char* id, enum id3_field_textencoding* encoding) { int nstrings, j; union id3_field const *field; struct id3_frame const *frame; id3_ucs4_list_t *list; frame = id3_tag_findframe(tag, id, 0); if (frame == 0) return 0; *encoding = id3_field_gettextencoding(id3_frame_field(frame, 0)); field = id3_frame_field(frame, 1); if (field == 0) return 0; nstrings = id3_field_getnstrings(field); list = 0; if (nstrings) { list = (id3_ucs4_list_t*)malloc(sizeof(*list)); if (list) list->strings = (const id3_ucs4_t**)malloc(nstrings * sizeof(*list->strings)); } if (list && list->strings) { list->nstrings = nstrings; for (j = 0; j < list->nstrings; ++j) list->strings[j] = id3_field_getstrings(field, j); } return list; }
static void write_tags(ImlibImage * im, lopt * opt) { struct id3_frame *frame = id3_tag_get_frame(opt->ctx->tag, opt->index - 1); union id3_field *field; int num_data; char *data; if ((field = id3_frame_field(frame, 1)) && (data = (char *)id3_field_getlatin1(field))) __imlib_AttachTag(im, "mime-type", 0, strdup(data), destructor_data); if ((field = id3_frame_field(frame, 3)) && (data = (char *)id3_field_getstring(field))) { size_t length; char *dup; id3_ucs4_t *ptr = (id3_ucs4_t *) data; while (*ptr) ptr++; length = (ptr - (id3_ucs4_t *) data + 1) * sizeof(id3_ucs4_t); dup = (char *)malloc(length); memcpy(dup, data, length); __imlib_AttachTag(im, "id3-description", 0, dup, destructor_data); } if (field = id3_frame_field(frame, 0)) __imlib_AttachTag(im, "id3-description-text-encoding", (num_data = (int)id3_field_gettextencoding(field)), num_data < NUM_OF_ID3_TEXT_ENCODINGS ? id3_text_encodings[num_data] : NULL, NULL); if (field = id3_frame_field(frame, 2)) __imlib_AttachTag(im, "id3-picture-type", (num_data = id3_field_getint(field)), num_data < NUM_OF_ID3_PIC_TYPES ? id3_pic_types[num_data] : NULL, NULL); __imlib_AttachTag(im, "count", id3_tag_get_numframes(opt->ctx->tag), NULL, NULL); if (opt->cache_level) { context_addref(opt->ctx); __imlib_AttachTag(im, "context", opt->ctx->id, opt->ctx, destructor_context); } __imlib_AttachTag(im, "index", opt->index, NULL, NULL); if (opt->traverse) { char *buf = NULL; if ((opt->index + opt->traverse) <= id3_tag_get_numframes(opt->ctx->tag) && (opt->index + opt->traverse) > 0) { buf = (char *)malloc((strlen(im->real_file) + 50) * sizeof(char)); sprintf(buf, "%s:index=%d,traverse=%d", im->real_file, opt->index + opt->traverse, opt->traverse); } __imlib_AttachTag(im, "next", 0, buf, destructor_data); } }
const id3_ucs4_t* id3_metadata_getcomment(const struct id3_tag* tag, enum id3_field_textencoding* encoding) { union id3_field const *field; struct id3_frame const *frame; int commentNumber = 0; const id3_ucs4_t* ucs4 = 0; // return the first non-empty comment do { frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, commentNumber++); if (frame && frame->nfields == 4) { //get short description field = id3_frame_field(frame, 2); if (field == 0) continue; ucs4 = id3_field_getstring(field); // Multiple values are allowed per comment field, but storing different comment // frames requires a different description for each frame. The first COMM frame // encountered without a description will be used as the comment field. // Source http://puddletag.sourceforge.net/source/id3.html if (ucs4 && *ucs4 == 0)//if short description on this frame is empty - consider this the wanted comment frame { //fetch encoding of the frame field = id3_frame_field(frame, 0); if(field == 0) continue; *encoding = id3_field_gettextencoding(field); //finally fetch the comment field = id3_frame_field(frame, 3); if (field == 0) continue; return id3_field_getfullstring(field); } } } while (frame); return ucs4; }
static char *get_tag (struct id3_tag *tag, const char *what) { struct id3_frame *frame; union id3_field *field; const id3_ucs4_t *ucs4; char *comm = NULL; frame = id3_tag_findframe (tag, what, 0); if (frame && (field = &frame->fields[1])) { ucs4 = id3_field_getstrings (field, 0); if (ucs4) { /* Workaround for ID3 tags v1/v1.1 where the encoding * is latin1. */ union id3_field *encoding_field = &frame->fields[0]; if ((id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1) || ((options_get_int ("EnforceTagsEncoding") && (id3_field_gettextencoding((encoding_field)) == ID3_FIELD_TEXTENCODING_ISO_8859_1)))) { char *t; comm = (char *)id3_ucs4_latin1duplicate (ucs4); #ifdef HAVE_RCC if (options_get_int("UseRCC")) comm = do_rcc (comm); else { #endif /* HAVE_RCC */ t = comm; comm = id3v1_fix (comm); free (t); #ifdef HAVE_RCC } #endif /* HAVE_RCC */ } else comm = (char *)id3_ucs4_utf8duplicate (ucs4); } } return comm; }
wchar_t* GetMP3Tag(const id3_tag* tag, const char* name) { wchar_t* content = NULL; id3_frame* frame = id3_tag_findframe(tag, name, 0); if (frame != NULL) { id3_field* field = id3_frame_field(frame, 0); id3_field_textencoding encoding = id3_field_gettextencoding(field); field = id3_frame_field(frame, 1); switch (id3_field_type(field)) { case ID3_FIELD_TYPE_STRING: content = GetMP3Text(encoding, field, id3_field_getstring(field)); break; case ID3_FIELD_TYPE_STRINGFULL: content = GetMP3Text(encoding, field, id3_field_getfullstring(field)); break; case ID3_FIELD_TYPE_STRINGLIST: { DWORD dataLength = 0, bufferLength = 0; unsigned int n = id3_field_getnstrings(field); for (unsigned int i = 0; i < n; i++) { wchar_t* p = GetMP3Text(encoding, field, id3_field_getstrings(field, i)); if (p == NULL) continue; AppendBuffer((char**)&content, dataLength, bufferLength, (const char*)p, wcslen(p) * 2 + 1); SAFE_DELETE_ARRAY(p); } } break; } } return content; }
int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) { struct id3_file *pid3file; struct id3_tag *pid3tag; struct id3_frame *pid3frame; int err; int index; int used; char *utf8_text; int genre=WINAMP_GENRE_UNKNOWN; int have_utf8; int have_text; id3_ucs4_t const *native_text; char *tmp; int got_numeric_genre; int rating; char *conversion_codepage; pid3file=id3_file_open(file,ID3_FILE_MODE_READONLY); if(!pid3file) { DPRINTF(E_WARN,L_SCAN,"Cannot open %s\n",file); return FALSE; } pid3tag=id3_file_tag(pid3file); if(!pid3tag) { err=errno; id3_file_close(pid3file); errno=err; DPRINTF(E_WARN,L_SCAN,"Cannot get ID3 tag for %s\n",file); return FALSE; } DPRINTF(E_SPAM,L_SCAN,"Starting mp3 tag scan\n"); conversion_codepage = conf_alloc_string("scanning","mp3_tag_codepage", "ISO-8859-1"); index=0; while((pid3frame=id3_tag_findframe(pid3tag,"",index))) { used=0; utf8_text=NULL; native_text=NULL; have_utf8=0; have_text=0; DPRINTF(E_SPAM,L_SCAN,"Found tag %s\n",pid3frame->id); if(!strcmp(pid3frame->id,"YTCP")) { /* for id3v2.2 */ pmp3->compilation = 1; DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation); } if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) && (id3_field_getnstrings(&pid3frame->fields[1]))) have_text=1; if(have_text) { native_text=id3_field_getstrings(&pid3frame->fields[1],0); if(native_text) { have_utf8=1; utf8_text = (char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) mem_register(utf8_text,0); if(id3_field_gettextencoding(&pid3frame->fields[1]) == ID3_FIELD_TEXTENCODING_ISO_8859_1) { #ifdef HAVE_ICONV /* this is kinda cheesy, but ucs4* == char* for 8859-1 */ free(utf8_text); utf8_text = (char*)util_xtoutf8_alloc((unsigned char*)native_text, strlen((char*)native_text), conversion_codepage); #endif } if(!strcmp(pid3frame->id,"TIT2")) { /* Title */ used=1; pmp3->title = utf8_text; DPRINTF(E_DBG,L_SCAN," Title: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TPE1")) { used=1; pmp3->artist = utf8_text; DPRINTF(E_DBG,L_SCAN," Artist: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TALB")) { used=1; pmp3->album = utf8_text; DPRINTF(E_DBG,L_SCAN," Album: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TCOM")) { used=1; pmp3->composer = utf8_text; DPRINTF(E_DBG,L_SCAN," Composer: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TIT1")) { used=1; pmp3->grouping = utf8_text; DPRINTF(E_DBG,L_SCAN," Grouping: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TPE2")) { used=1; pmp3->orchestra = utf8_text; DPRINTF(E_DBG,L_SCAN," Orchestra: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TPE3")) { used=1; pmp3->conductor = utf8_text; DPRINTF(E_DBG,L_SCAN," Conductor: %s\n",utf8_text); } else if(!strcmp(pid3frame->id,"TCON")) { used=1; pmp3->genre = utf8_text; got_numeric_genre=0; DPRINTF(E_DBG,L_SCAN," Genre: %s\n",utf8_text); if(pmp3->genre) { if(!strlen(pmp3->genre)) { genre=WINAMP_GENRE_UNKNOWN; got_numeric_genre=1; } else if (scan_mp3_is_numeric(pmp3->genre)) { genre=atoi(pmp3->genre); got_numeric_genre=1; } else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) { genre=atoi((char*)&pmp3->genre[1]); got_numeric_genre=1; } if(got_numeric_genre) { if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) genre=WINAMP_GENRE_UNKNOWN; free(pmp3->genre); pmp3->genre=strdup(scan_winamp_genre[genre]); } } } else if(!strcmp(pid3frame->id,"COMM")) { used=1; pmp3->comment = utf8_text; DPRINTF(E_DBG,L_SCAN," Comment: %s\n",pmp3->comment); } else if(!strcmp(pid3frame->id,"TPOS")) { tmp=utf8_text; strsep(&tmp,"/"); if(tmp) { pmp3->total_discs=atoi(tmp); } pmp3->disc=atoi(utf8_text); DPRINTF(E_DBG,L_SCAN," Disc %d of %d\n",pmp3->disc,pmp3->total_discs); } else if(!strcmp(pid3frame->id,"TRCK")) { tmp=utf8_text; strsep(&tmp,"/"); if(tmp) { pmp3->total_tracks=atoi(tmp); } pmp3->track=atoi(utf8_text); DPRINTF(E_DBG,L_SCAN," Track %d of %d\n",pmp3->track,pmp3->total_tracks); } else if(!strcmp(pid3frame->id,"TDRC")) { pmp3->year = atoi(utf8_text); DPRINTF(E_DBG,L_SCAN," Year: %d\n",pmp3->year); } else if(!strcmp(pid3frame->id,"TLEN")) { pmp3->song_length = atoi(utf8_text); /* now in ms */ DPRINTF(E_DBG,L_SCAN," Length: %d\n", pmp3->song_length); } else if(!strcmp(pid3frame->id,"TBPM")) { pmp3->bpm = atoi(utf8_text); DPRINTF(E_DBG,L_SCAN,"BPM: %d\n", pmp3->bpm); } else if(!strcmp(pid3frame->id,"TCMP")) { /* for id3v2.3 */ pmp3->compilation = (char)atoi(utf8_text); DPRINTF(E_DBG,L_SCAN,"Compilation: %d\n", pmp3->compilation); } } } /* can check for non-text tags here */ if((!used) && (have_utf8) && (utf8_text)) free(utf8_text); if((!strcmp(pid3frame->id,"POPM")) && (pid3frame->nfields == 3)) { rating = id3_field_getint(&pid3frame->fields[1]); if(rating >= 0x01) pmp3->rating = 20; if(rating >= 0x40) pmp3->rating = 40; if(rating >= 0x80) pmp3->rating = 60; if(rating >= 0xC4) pmp3->rating = 80; if(rating >= 0xFF) pmp3->rating = 100; } /* v2 COMM tags are a bit different than v1 */ if((!strcmp(pid3frame->id,"COMM")) && (pid3frame->nfields == 4)) { /* Make sure it isn't a application-specific comment... * This currently includes the following: * * iTunes_CDDB_IDs * iTunNORM * * If other apps stuff crap into comment fields, then we'll ignore them * here. */ native_text=id3_field_getstring(&pid3frame->fields[2]); if(native_text) { utf8_text=(char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) mem_register(utf8_text,0); if((utf8_text) && (strncasecmp(utf8_text,"iTun",4) != 0)) { /* it's a real comment */ if(utf8_text) free(utf8_text); native_text=id3_field_getfullstring(&pid3frame->fields[3]); if(native_text) { if(pmp3->comment) free(pmp3->comment); utf8_text=(char*)id3_ucs4_utf8duplicate(native_text); if(utf8_text) { mem_register(utf8_text,0); pmp3->comment=utf8_text; } } } else { if(utf8_text) free(utf8_text); } } } index++; } free(conversion_codepage); id3_file_close(pid3file); DPRINTF(E_DBG,L_SCAN,"Got id3 tag successfully\n"); return TRUE; }
static void getID3Info(struct id3_tag *tag, const char *id, int type, struct tag *mpdTag) { struct id3_frame const *frame; id3_ucs4_t const *ucs4; id3_utf8_t *utf8; union id3_field const *field; unsigned int nstrings, i; enum id3_field_textencoding id3_encoding = 0xff; frame = id3_tag_findframe(tag, id, 0); /* Check frame */ if (!frame) { return; } /* Check fields in frame */ if(frame->nfields == 0) { g_debug("Frame has no fields"); return; } /* Starting with T is a stringlist */ if (id[0] == 'T') { /* This one contains 2 fields: * 1st: Text encoding * 2: Stringlist * Shamefully this isn't the RL case. * But I am going to enforce it anyway. */ if(frame->nfields != 2) { g_debug("Invalid number '%i' of fields for TXX frame", frame->nfields); return; } field = &frame->fields[0]; /** * First field is encoding field. * This is ignored by mpd. */ if(field->type != ID3_FIELD_TYPE_TEXTENCODING) { g_debug("Expected encoding, found: %i", field->type); } else id3_encoding = field->number.value; /* Process remaining fields, should be only one */ field = &frame->fields[1]; /* Encoding field */ if(field->type == ID3_FIELD_TYPE_STRINGLIST) { /* Get the number of strings available */ nstrings = id3_field_getnstrings(field); for (i = 0; i < nstrings; i++) { ucs4 = id3_field_getstrings(field,i); if(!ucs4) continue; utf8 = processID3FieldString(isId3v1(tag),ucs4, type, id3_encoding); if(!utf8) continue; tag_add_item(mpdTag, type, (char *)utf8); g_free(utf8); } } else { g_warning("Field type not processed: %i", (int)id3_field_gettextencoding(field)); } } /* A comment frame */ else if(!strcmp(ID3_FRAME_COMMENT, id)) { /* A comment frame is different... */ /* 1st: encoding * 2nd: Language * 3rd: String * 4th: FullString. * The 'value' we want is in the 4th field */ if(frame->nfields == 4) { /* for now I only read the 4th field, with the fullstring */ field = &frame->fields[3]; if(field->type == ID3_FIELD_TYPE_STRINGFULL) { ucs4 = id3_field_getfullstring(field); if(ucs4) { utf8 = processID3FieldString(isId3v1(tag),ucs4, type, id3_encoding); if(utf8) { tag_add_item(mpdTag, type, (char *)utf8); g_free(utf8); } } } else { g_debug("4th field in comment frame differs from expected, got '%i': ignoring", field->type); } } else { g_debug("Invalid 'comments' tag, got '%i' fields instead of 4", frame->nfields); } } /* Unsupported */ else g_debug("Unsupported tag type requrested"); }