Merge branch 'master' of git://staff.get-e.org/users/andrunko/lightmediascanner
[lms] / lightmediascanner / src / plugins / id3 / id3.c
1 /**
2  * Copyright (C) 2008 by INdT
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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 Lesser General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * id3 file parser.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #define _XOPEN_SOURCE 600
32 #include <lightmediascanner_plugin.h>
33 #include <lightmediascanner_db.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <ctype.h>
42
43 #define ID3V2_HEADER_SIZE       10
44 #define ID3V2_FOOTER_SIZE       10
45 #define ID3V2_FRAME_HEADER_SIZE 10
46
47 #define ID3V1_NUM_GENRES 148
48
49 static const char *id3v1_genres[ID3V1_NUM_GENRES] = {
50     "Blues",
51     "Classic Rock",
52     "Country",
53     "Dance",
54     "Disco",
55     "Funk",
56     "Grunge",
57     "Hip-Hop",
58     "Jazz",
59     "Metal",
60     "New Age",
61     "Oldies",
62     "Other",
63     "Pop",
64     "R&B",
65     "Rap",
66     "Reggae",
67     "Rock",
68     "Techno",
69     "Industrial",
70     "Alternative",
71     "Ska",
72     "Death Metal",
73     "Pranks",
74     "Soundtrack",
75     "Euro-Techno",
76     "Ambient",
77     "Trip-Hop",
78     "Vocal",
79     "Jazz+Funk",
80     "Fusion",
81     "Trance",
82     "Classical",
83     "Instrumental",
84     "Acid",
85     "House",
86     "Game",
87     "Sound Clip",
88     "Gospel",
89     "Noise",
90     "Alternative Rock",
91     "Bass",
92     "Soul",
93     "Punk",
94     "Space",
95     "Meditative",
96     "Instrumental Pop",
97     "Instrumental Rock",
98     "Ethnic",
99     "Gothic",
100     "Darkwave",
101     "Techno-Industrial",
102     "Electronic",
103     "Pop-Folk",
104     "Eurodance",
105     "Dream",
106     "Southern Rock",
107     "Comedy",
108     "Cult",
109     "Gangsta",
110     "Top 40",
111     "Christian Rap",
112     "Pop/Funk",
113     "Jungle",
114     "Native American",
115     "Cabaret",
116     "New Wave",
117     "Psychedelic",
118     "Rave",
119     "Showtunes",
120     "Trailer",
121     "Lo-Fi",
122     "Tribal",
123     "Acid Punk",
124     "Acid Jazz",
125     "Polka",
126     "Retro",
127     "Musical",
128     "Rock & Roll",
129     "Hard Rock",
130     "Folk",
131     "Folk/Rock",
132     "National Folk",
133     "Swing",
134     "Fusion",
135     "Bebob",
136     "Latin",
137     "Revival",
138     "Celtic",
139     "Bluegrass",
140     "Avantgarde",
141     "Gothic Rock",
142     "Progressive Rock",
143     "Psychedelic Rock",
144     "Symphonic Rock",
145     "Slow Rock",
146     "Big Band",
147     "Chorus",
148     "Easy Listening",
149     "Acoustic",
150     "Humour",
151     "Speech",
152     "Chanson",
153     "Opera",
154     "Chamber Music",
155     "Sonata",
156     "Symphony",
157     "Booty Bass",
158     "Primus",
159     "Porn Groove",
160     "Satire",
161     "Slow Jam",
162     "Club",
163     "Tango",
164     "Samba",
165     "Folklore",
166     "Ballad",
167     "Power Ballad",
168     "Rhythmic Soul",
169     "Freestyle",
170     "Duet",
171     "Punk Rock",
172     "Drum Solo",
173     "A Cappella",
174     "Euro-House",
175     "Dance Hall",
176     "Goa",
177     "Drum & Bass",
178     "Club-House",
179     "Hardcore",
180     "Terror",
181     "Indie",
182     "BritPop",
183     "Negerpunk",
184     "Polsk Punk",
185     "Beat",
186     "Christian Gangsta Rap",
187     "Heavy Metal",
188     "Black Metal",
189     "Crossover",
190     "Contemporary Christian",
191     "Christian Rock",
192     "Merengue",
193     "Salsa",
194     "Thrash Metal",
195     "Anime",
196     "Jpop",
197     "Synthpop"
198 };
199
200 struct id3v2_frame_header {
201     char frame_id[4];
202     unsigned int frame_size;
203     int compression;
204     int data_length_indicator;
205 };
206
207 struct id3v1_tag {
208     char title[30];
209     char artist[30];
210     char album[30];
211     char year[4];
212     char comments[30];
213     char genre;
214 } __attribute__((packed));
215
216 struct plugin {
217     struct lms_plugin plugin;
218     lms_db_audio_t *audio_db;
219 };
220
221 static const char _name[] = "id3";
222 static const struct lms_string_size _exts[] = {
223     LMS_STATIC_STRING_SIZE(".mp3"),
224     LMS_STATIC_STRING_SIZE(".aac")
225 };
226
227 static unsigned int
228 _to_uint(const char *data, int data_size)
229 {
230     unsigned int sum = 0;
231     int last = data_size > 4 ? 3 : data_size - 1;
232     int i;
233
234     for (i = 0; i <= last; i++)
235         sum |= (data[i] & 0x7f) << ((last - i) * 7);
236
237     return sum;
238 }
239
240 static inline int
241 _is_id3v2_second_synch_byte(unsigned char byte)
242 {
243     if (byte == 0xff)
244         return 0;
245     if ((byte & 0xE0) == 0xE0)
246         return 1;
247     return 0;
248 }
249
250 static long
251 _find_id3v2(int fd)
252 {
253     long buffer_offset = 0;
254     char buffer[3], *p;
255     int buffer_size = sizeof(buffer);
256     const char pattern[] = "ID3";
257     ssize_t nread;
258
259     /* These variables are used to keep track of a partial match that happens at
260      * the end of a buffer. */
261     int previous_partial_match = -1;
262     int previous_partial_synch_match = 0;
263     int first_synch_byte;
264
265     /* Start the search at the beginning of the file. */
266     lseek(fd, 0, SEEK_SET);
267
268     if ((nread = read(fd, &buffer, buffer_size)) != buffer_size)
269         return -1;
270
271     /* check if pattern is in the beggining of the file */
272     if (memcmp(buffer, pattern, 3) == 0)
273         return 0;
274
275     /* This loop is the crux of the find method.  There are three cases that we
276      * want to account for:
277      * (1) The previously searched buffer contained a partial match of the search
278      * pattern and we want to see if the next one starts with the remainder of
279      * that pattern.
280      *
281      * (2) The search pattern is wholly contained within the current buffer.
282      *
283      * (3) The current buffer ends with a partial match of the pattern.  We will
284      * note this for use in the next iteration, where we will check for the rest
285      * of the pattern. */
286     while (1) {
287         /* (1) previous partial match */
288         if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
289             return -1;
290
291         if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
292             const int pattern_offset = buffer_size - previous_partial_match;
293
294             if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
295                 return buffer_offset - buffer_size + previous_partial_match;
296         }
297
298         /* (2) pattern contained in current buffer */
299         p = buffer;
300         while ((p = memchr(p, 'I', buffer_size))) {
301             if (memcmp(p, pattern, 3) == 0)
302                 return buffer_offset + (p - buffer);
303             p += 1;
304         }
305
306         p = memchr(buffer, 255, buffer_size);
307         if (p)
308             first_synch_byte = p - buffer;
309         else
310             first_synch_byte = -1;
311
312         /* Here we have to loop because there could be several of the first
313          * (11111111) byte, and we want to check all such instances until we find
314          * a full match (11111111 111) or hit the end of the buffer. */
315         while (first_synch_byte >= 0) {
316             /* if this *is not* at the end of the buffer */
317             if (first_synch_byte < buffer_size - 1) {
318                 if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
319                     /* We've found the frame synch pattern. */
320                     return -1;
321                 else
322                     /* We found 11111111 at the end of the current buffer indicating a
323                      * partial match of the synch pattern.  The find() below should
324                      * return -1 and break out of the loop. */
325                     previous_partial_synch_match = 1;
326             }
327
328             /* Check in the rest of the buffer. */
329             p = memchr(p + 1, 255, buffer_size);
330             if (p)
331                 first_synch_byte = p - buffer;
332             else
333                 first_synch_byte = -1;
334         }
335
336         /* (3) partial match */
337         if (buffer[nread - 1] == pattern[1])
338             previous_partial_match = nread - 1;
339         else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
340             previous_partial_match = nread - 2;
341         buffer_offset += buffer_size;
342
343         if ((nread = read(fd, &buffer, sizeof(buffer))) == -1)
344             return -1;
345     }
346
347     return -1;
348 }
349
350 static unsigned int
351 _get_id3v2_frame_header_size(unsigned int version)
352 {
353     switch (version) {
354     case 0:
355     case 1:
356     case 2:
357         return 6;
358     case 3:
359     case 4:
360     default:
361         return 10;
362     }
363 }
364
365 static void
366 _parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
367 {
368     switch (version) {
369     case 0:
370     case 1:
371     case 2:
372         memcpy(fh->frame_id, data, 3);
373         fh->frame_id[3] = 0;
374         fh->frame_size = _to_uint(data + 3, 3);
375         fh->compression = 0;
376         fh->data_length_indicator = 0;
377         break;
378     case 3:
379         memcpy(fh->frame_id, data, 4);
380         fh->frame_size = _to_uint(data + 4, 4);
381         fh->compression = data[9] & 0x40;
382         fh->data_length_indicator = 0;
383         break;
384     case 4:
385     default:
386         memcpy(fh->frame_id, data, 4);
387         fh->frame_size = _to_uint(data + 4, 4);
388         fh->compression = data[9] & 0x4;
389         fh->data_length_indicator = data[9] & 0x1;
390         break;
391     }
392 }
393
394 static inline void
395 _get_id3v2_frame_info(const char *frame_data, unsigned int frame_size, struct lms_string_size *s)
396 {
397     s->str = malloc(sizeof(char) * (frame_size + 1));
398     memcpy(s->str, frame_data, frame_size);
399     s->str[frame_size] = '\0';
400     s->len = frame_size;
401 }
402
403 static void
404 _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct lms_audio_info *info)
405 {
406     unsigned int frame_size;
407
408     /* TODO proper handle text encoding
409      *
410      * Latin1  = 0
411      * UTF16   = 1
412      * UTF16BE = 2
413      * UTF8    = 3
414      * UTF16LE = 4
415      */
416
417     /* skip first byte - text encoding */
418     frame_data += 1;
419     frame_size = fh->frame_size - 1;
420
421     if (memcmp(fh->frame_id, "TIT2", 4) == 0)
422         _get_id3v2_frame_info(frame_data, frame_size, &info->title);
423     else if (memcmp(fh->frame_id, "TPE1", 4) == 0)
424         _get_id3v2_frame_info(frame_data, frame_size, &info->artist);
425     else if (memcmp(fh->frame_id, "TALB", 4) == 0)
426         _get_id3v2_frame_info(frame_data, frame_size, &info->album);
427     else if (memcmp(fh->frame_id, "TCON", 4) == 0) {
428         /* TODO handle multiple genres */
429         int i, is_number;
430
431         _get_id3v2_frame_info(frame_data, frame_size, &info->genre);
432
433         is_number = 1;
434         for (i = 0; i < info->genre.len; ++i) {
435             if (!isdigit(info->genre.str[i]))
436                 is_number = 0;
437         }
438
439         if ((is_number) &&
440             (info->genre.str) &&
441             (info->genre.len > 0)) {
442             int genre = atoi(info->genre.str);
443             if (genre >= 0 && genre < ID3V1_NUM_GENRES) {
444                 free(info->genre.str);
445                 info->genre.str = strdup(id3v1_genres[(int) genre]);
446                 info->genre.len = strlen(info->genre.str);
447             }
448             else {
449                 /* ignore other genres */
450                 free(info->genre.str);
451                 info->genre.str = NULL;
452                 info->genre.len = 0;
453             }
454         }
455     }
456     else if (memcmp(fh->frame_id, "TRCK", 4) == 0) {
457         struct lms_string_size trackno;
458         _get_id3v2_frame_info(frame_data, frame_size, &trackno);
459         info->trackno = atoi(trackno.str);
460         free(trackno.str);
461     }
462 }
463
464 static int
465 _parse_id3v2(int fd, long id3v2_offset, struct lms_audio_info *info)
466 {
467     char header_data[10], frame_header_data[10];
468     unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
469     int extended_header, footer_present;
470     struct id3v2_frame_header fh;
471
472     lseek(fd, id3v2_offset, SEEK_SET);
473
474     /* parse header */
475     if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
476         return -1;
477
478     tag_size = _to_uint(header_data + 6, 4);
479     if (tag_size == 0)
480         return -1;
481
482     /* parse frames */
483     major_version = header_data[3];
484
485     frame_data_pos = 0;
486     frame_data_length = tag_size;
487
488     /* check for extended header */
489     extended_header = header_data[5] & 0x20; /* bit 6 */
490     if (extended_header) {
491         /* skip extended header */
492         unsigned int extended_header_size;
493         char extended_header_data[4];
494
495         if (read(fd, extended_header_data, 4) != 4)
496             return -1;
497         extended_header_size = _to_uint(extended_header_data, 4);
498         lseek(fd, extended_header_size - 4, SEEK_CUR);
499         frame_data_pos += extended_header_size;
500         frame_data_length -= extended_header_size;
501     }
502
503     footer_present = header_data[5] & 0x8;   /* bit 4 */
504     if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
505         frame_data_length -= ID3V2_FOOTER_SIZE;
506
507     frame_header_size = _get_id3v2_frame_header_size(major_version);
508     while (frame_data_pos < frame_data_length - frame_header_size) {
509         if (read(fd, frame_header_data, ID3V2_FRAME_HEADER_SIZE) != ID3V2_FRAME_HEADER_SIZE)
510             return -1;
511
512         if (frame_header_data[0] == 0)
513             break;
514
515         _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
516
517         if (!fh.compression &&
518             fh.frame_id[0] == 'T' &&
519             memcmp(fh.frame_id, "TXXX", 4) != 0) {
520             char *frame_data;
521
522             if (fh.data_length_indicator)
523                 lseek(fd, 4, SEEK_CUR);
524
525             frame_data = malloc(sizeof(char) * fh.frame_size);
526             if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
527                 free(frame_data);
528                 return -1;
529             }
530
531             _parse_id3v2_frame(&fh, frame_data, info);
532             free(frame_data);
533         }
534         else {
535             if (fh.data_length_indicator)
536                 lseek(fd, fh.frame_size + 4, SEEK_CUR);
537             else
538                 lseek(fd, fh.frame_size, SEEK_CUR);
539         }
540
541         frame_data_pos += fh.frame_size + frame_header_size;
542     }
543
544     return 0;
545 }
546
547 static int
548 _parse_id3v1(int fd, struct lms_audio_info *info)
549 {
550     struct id3v1_tag tag;
551     if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
552         return -1;
553
554     info->title.str = strdup(tag.title);
555     info->title.len = strlen(info->title.str);
556     info->artist.str = strdup(tag.artist);
557     info->artist.len = strlen(info->artist.str);
558     info->album.str = strdup(tag.album);
559     info->album.len = strlen(info->album.str);
560     info->genre.str = strdup(id3v1_genres[(int) tag.genre]);
561     info->genre.len = strlen(info->genre.str);
562     if (tag.comments[28] == 0 && tag.comments[29] != 0)
563         info->trackno = (unsigned char) tag.comments[29];
564
565     return 0;
566 }
567
568 static void *
569 _match(struct plugin *p, const char *path, int len, int base)
570 {
571     int i;
572
573     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
574     if (i < 0)
575       return NULL;
576     else
577       return (void*)(i + 1);
578 }
579
580 static int
581 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
582 {
583     struct lms_audio_info info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
584     int r, fd;
585     long id3v2_offset;
586
587     fd = open(finfo->path, O_RDONLY);
588     if (fd < 0) {
589         perror("open");
590         return -1;
591     }
592
593     id3v2_offset = _find_id3v2(fd);
594     if (id3v2_offset >= 0) {
595 #if 0
596         fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
597                 finfo->path, id3v2_offset);
598 #endif
599         if (_parse_id3v2(fd, id3v2_offset, &info) != 0) {
600             r = -2;
601             goto done;
602         }
603     }
604     else {
605         char tag[3];
606 #if 0
607         fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
608 #endif
609         /* check for id3v1 tag */
610         if (lseek(fd, -128, SEEK_END) == -1) {
611             r = -3;
612             goto done;
613         }
614
615         if (read(fd, &tag, 3) == -1) {
616             r = -4;
617             goto done;
618         }
619
620         if (memcmp(tag, "TAG", 3) == 0) {
621 #if 0
622             fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
623 #endif
624             if (_parse_id3v1(fd, &info) != 0) {
625                 r = -5;
626                 goto done;
627             }
628         }
629     }
630
631     lms_string_size_strip_and_free(&info.title);
632     lms_string_size_strip_and_free(&info.artist);
633     lms_string_size_strip_and_free(&info.album);
634     lms_string_size_strip_and_free(&info.genre);
635
636     if (!info.title.str) {
637         int ext_idx;
638         ext_idx = ((int)match) - 1;
639         info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
640         info.title.str = malloc((info.title.len + 1) * sizeof(char));
641         memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
642         info.title.str[info.title.len] = '\0';
643     }
644     lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
645
646     if (info.artist.str)
647         lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
648     if (info.album.str)
649         lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
650     if (info.genre.str)
651         lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
652
653 #if 0
654     fprintf(stderr, "file %s info\n", finfo->path);
655     fprintf(stderr, "\ttitle='%s'\n", info.title.str);
656     fprintf(stderr, "\tartist='%s'\n", info.artist.str);
657     fprintf(stderr, "\talbum='%s'\n", info.album.str);
658     fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
659     fprintf(stderr, "\ttrack number='%d'\n", info.trackno);
660 #endif
661
662     info.id = finfo->id;
663     r = lms_db_audio_add(plugin->audio_db, &info);
664
665   done:
666     close(fd);
667
668     if (info.title.str)
669         free(info.title.str);
670     if (info.artist.str)
671         free(info.artist.str);
672     if (info.album.str)
673         free(info.album.str);
674     if (info.genre.str)
675         free(info.genre.str);
676
677     return r;
678 }
679
680 static int
681 _setup(struct plugin *plugin, struct lms_context *ctxt)
682 {
683     plugin->audio_db = lms_db_audio_new(ctxt->db);
684     if (!plugin->audio_db)
685         return -1;
686     return 0;
687 }
688
689 static int
690 _start(struct plugin *plugin, struct lms_context *ctxt)
691 {
692     return lms_db_audio_start(plugin->audio_db);
693 }
694
695 static int
696 _finish(struct plugin *plugin, struct lms_context *ctxt)
697 {
698     if (plugin->audio_db)
699         lms_db_audio_free(plugin->audio_db);
700     return 0;
701 }
702
703 static int
704 _close(struct plugin *plugin)
705 {
706     free(plugin);
707     return 0;
708 }
709
710 API struct lms_plugin *
711 lms_plugin_open(void)
712 {
713     struct plugin *plugin;
714
715     plugin = (struct plugin *)malloc(sizeof(*plugin));
716     plugin->plugin.name = _name;
717     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
718     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
719     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
720     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
721     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
722     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
723
724     return (struct lms_plugin *)plugin;
725 }