1//========================================================================= 2// FILENAME : tagutils.c 3// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader 4//========================================================================= 5// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved. 6//========================================================================= 7 8/* This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23/* This file is derived from mt-daapd project */ 24 25#include <ctype.h> 26#include <errno.h> 27#include <id3tag.h> 28#include <stdlib.h> 29#include <stddef.h> 30#include <string.h> 31#include <fcntl.h> 32#include <unistd.h> 33#include <time.h> 34#include <sys/time.h> 35#include <netinet/in.h> 36#include <ogg/ogg.h> 37#include <vorbis/codec.h> 38#include <FLAC/metadata.h> 39 40#include "../config.h" 41#ifdef HAVE_ICONV_H 42#include <iconv.h> 43#endif 44 45#include <sqlite3.h> 46#include "tagutils.h" 47#include "misc.h" 48#include "textutils.h" 49#include "../metadata.h" 50#include "../log.h" 51 52struct id3header { 53 unsigned char id[3]; 54 unsigned char version[2]; 55 unsigned char flags; 56 unsigned char size[4]; 57} __attribute((packed)); 58 59char *winamp_genre[] = { 60 /*00*/ "Blues", "Classic Rock", "Country", "Dance", 61 "Disco", "Funk", "Grunge", "Hip-Hop", 62 /*08*/ "Jazz", "Metal", "New Age", "Oldies", 63 "Other", "Pop", "R&B", "Rap", 64 /*10*/ "Reggae", "Rock", "Techno", "Industrial", 65 "Alternative", "Ska", "Death Metal", "Pranks", 66 /*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", 67 "Vocal", "Jazz+Funk", "Fusion", "Trance", 68 /*20*/ "Classical", "Instrumental", "Acid", "House", 69 "Game", "Sound Clip", "Gospel", "Noise", 70 /*28*/ "AlternRock", "Bass", "Soul", "Punk", 71 "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", 72 /*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", 73 "Electronic", "Pop-Folk", "Eurodance", "Dream", 74 /*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta", 75 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", 76 /*40*/ "Native American", "Cabaret", "New Wave", "Psychadelic", 77 "Rave", "Showtunes", "Trailer", "Lo-Fi", 78 /*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka", 79 "Retro", "Musical", "Rock & Roll", "Hard Rock", 80 /*50*/ "Folk", "Folk/Rock", "National folk", "Swing", 81 "Fast-fusion", "Bebob", "Latin", "Revival", 82 /*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", 83 "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", 84 /*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic", 85 "Humour", "Speech", "Chanson", "Opera", 86 /*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass", 87 "Primus", "Porn Groove", "Satire", "Slow Jam", 88 /*70*/ "Club", "Tango", "Samba", "Folklore", 89 "Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle", 90 /*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella", 91 "Euro-House", "Dance Hall", "Goa", "Drum & Bass", 92 /*80*/ "Club House", "Hardcore", "Terror", "Indie", 93 "BritPop", "NegerPunk", "Polsk Punk", "Beat", 94 /*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", 95 "Contemporary C", "Christian Rock", "Merengue", "Salsa", 96 /*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop", 97 "Unknown" 98}; 99 100#define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1) 101 102 103/* 104 * Prototype 105 */ 106#include "tagutils-mp3.h" 107#include "tagutils-aac.h" 108#include "tagutils-ogg.h" 109#include "tagutils-flc.h" 110#include "tagutils-asf.h" 111#include "tagutils-wav.h" 112#include "tagutils-pcm.h" 113 114static int _get_tags(char *file, struct song_metadata *psong); 115static int _get_fileinfo(char *file, struct song_metadata *psong); 116 117 118/* 119 * Typedefs 120 */ 121 122typedef struct { 123 char* type; 124 int (*get_tags)(char* file, struct song_metadata* psong); 125 int (*get_fileinfo)(char* file, struct song_metadata* psong); 126} taghandler; 127 128static taghandler taghandlers[] = { 129 { "aac", _get_aactags, _get_aacfileinfo }, 130 { "mp3", _get_mp3tags, _get_mp3fileinfo }, 131 { "flc", _get_flctags, _get_flcfileinfo }, 132 { "ogg", 0, _get_oggfileinfo }, 133 { "asf", 0, _get_asffileinfo }, 134 { "wav", _get_wavtags, _get_wavfileinfo }, 135 { "pcm", 0, _get_pcmfileinfo }, 136 { NULL, 0 } 137}; 138 139 140 141//********************************************************************************* 142#include "tagutils-misc.c" 143#include "tagutils-mp3.c" 144#include "tagutils-aac.c" 145#include "tagutils-ogg.c" 146#include "tagutils-flc.c" 147#include "tagutils-asf.c" 148#include "tagutils-wav.c" 149#include "tagutils-pcm.c" 150#include "tagutils-plist.c" 151 152//********************************************************************************* 153// freetags() 154#define MAYBEFREE(a) { if((a)) free((a)); }; 155void 156freetags(struct song_metadata *psong) 157{ 158 int role; 159 160 MAYBEFREE(psong->path); 161 MAYBEFREE(psong->image); 162 MAYBEFREE(psong->title); 163 MAYBEFREE(psong->album); 164 MAYBEFREE(psong->genre); 165 MAYBEFREE(psong->comment); 166 for(role = ROLE_START; role <= ROLE_LAST; role++) 167 { 168 MAYBEFREE(psong->contributor[role]); 169 MAYBEFREE(psong->contributor_sort[role]); 170 } 171 MAYBEFREE(psong->grouping); 172 MAYBEFREE(psong->mime); 173 MAYBEFREE(psong->dlna_pn); 174 MAYBEFREE(psong->tagversion); 175 MAYBEFREE(psong->musicbrainz_albumid); 176 MAYBEFREE(psong->musicbrainz_trackid); 177 MAYBEFREE(psong->musicbrainz_artistid); 178 MAYBEFREE(psong->musicbrainz_albumartistid); 179} 180 181// _get_fileinfo 182static int 183_get_fileinfo(char *file, struct song_metadata *psong) 184{ 185 taghandler *hdl; 186 187 // dispatch to appropriate tag handler 188 for(hdl = taghandlers; hdl->type; ++hdl) 189 if(!strcmp(hdl->type, psong->type)) 190 break; 191 192 if(hdl->get_fileinfo) 193 return hdl->get_fileinfo(file, psong); 194 195 return 0; 196} 197 198 199static void 200_make_composite_tags(struct song_metadata *psong) 201{ 202 int len; 203 204 len = 1; 205 206 if(!psong->contributor[ROLE_ARTIST] && 207 (psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR])) 208 { 209 if(psong->contributor[ROLE_BAND]) 210 len += strlen(psong->contributor[ROLE_BAND]); 211 if(psong->contributor[ROLE_CONDUCTOR]) 212 len += strlen(psong->contributor[ROLE_CONDUCTOR]); 213 214 len += 3; 215 216 psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1); 217 if(psong->contributor[ROLE_ARTIST]) 218 { 219 if(psong->contributor[ROLE_BAND]) 220 strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]); 221 222 if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR]) 223 strcat(psong->contributor[ROLE_ARTIST], " - "); 224 225 if(psong->contributor[ROLE_CONDUCTOR]) 226 strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]); 227 } 228 } 229 230#if 0 // already taken care of by scanner.c 231 if(!psong->title) 232 { 233 char *suffix; 234 psong->title = strdup(psong->basename); 235 suffix = strrchr(psong->title, '.'); 236 if(suffix) *suffix = '\0'; 237 } 238#endif 239} 240 241 242/*****************************************************************************/ 243// _get_tags 244static int 245_get_tags(char *file, struct song_metadata *psong) 246{ 247 taghandler *hdl; 248 249 // dispatch 250 for(hdl = taghandlers ; hdl->type ; ++hdl) 251 if(!strcasecmp(hdl->type, psong->type)) 252 break; 253 254 if(hdl->get_tags) 255 { 256 return hdl->get_tags(file, psong); 257 } 258 259 return 0; 260} 261 262/*****************************************************************************/ 263// readtags 264int 265readtags(char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type) 266{ 267 char *fname; 268 269 if(lang_index == -1) 270 lang_index = _lang2cp(lang); 271 272 memset((void*)psong, 0, sizeof(struct song_metadata)); 273 psong->path = strdup(path); 274 psong->type = type; 275 276 fname = strrchr(psong->path, '/'); 277 psong->basename = fname ? fname + 1 : psong->path; 278 279 if(stat) 280 { 281 if(!psong->time_modified) 282 psong->time_modified = stat->st_mtime; 283 psong->file_size = stat->st_size; 284 } 285 286 // get tag 287 if( _get_tags(path, psong) == 0 ) 288 { 289 _make_composite_tags(psong); 290 } 291 292 // get fileinfo 293 return _get_fileinfo(path, psong); 294} 295