Improved mp3 support.
authorbarbieri <barbieri@gmail.com>
Thu, 14 Feb 2008 21:39:11 +0000 (21:39 +0000)
committerbarbieri <barbieri@gmail.com>
Thu, 14 Feb 2008 21:39:11 +0000 (21:39 +0000)
Removed old id3lib plugin.
Renamed aac plugin to id3.
Use id3 (old aac) plugin to parse mp3 and aac files.
Improved id3 (old aac) plugin.

lightmediascanner/configure.ac
lightmediascanner/src/plugins/Makefile.am
lightmediascanner/src/plugins/aac/Makefile.am [deleted file]
lightmediascanner/src/plugins/aac/aac.c [deleted file]
lightmediascanner/src/plugins/flac/Makefile.am [new file with mode: 0644]
lightmediascanner/src/plugins/id3/Makefile.am [new file with mode: 0644]
lightmediascanner/src/plugins/id3/id3.c [new file with mode: 0644]
lightmediascanner/src/plugins/id3lib/Makefile.am [deleted file]
lightmediascanner/src/plugins/id3lib/id3lib.cpp [deleted file]

index 650e5c7..caeb129 100644 (file)
@@ -38,13 +38,6 @@ AC_CHECK_FUNCS(realpath)
 PKG_CHECK_MODULES(SQLITE3, [sqlite3 >= 3.3])
 
 # plugins checks
-# plugins checks
-
-AM_CONDITIONAL(HAVE_ID3LIB, false)
-define([CHECK_MODULE_ID3LIB],
-[
-        AC_LMS_CHECK_PKG(ID3LIB, id3lib, [], [ID3LIB=false])
-])
 
 AM_CONDITIONAL(HAVE_VORBIS, false)
 define([CHECK_MODULE_OGG],
@@ -70,7 +63,6 @@ define([CHECK_MODULE_FLAC],
 AC_LMS_OPTIONAL_MODULE([dummy], true)
 AC_LMS_OPTIONAL_MODULE([jpeg], true)
 AC_LMS_OPTIONAL_MODULE([png], true)
-AC_LMS_OPTIONAL_MODULE([id3lib], true, [CHECK_MODULE_ID3LIB])
 AC_LMS_OPTIONAL_MODULE([video-dummy], true)
 AC_LMS_OPTIONAL_MODULE([audio-dummy], true)
 AC_LMS_OPTIONAL_MODULE([m3u], true)
@@ -79,7 +71,7 @@ AC_LMS_OPTIONAL_MODULE([pls], true)
 AC_LMS_OPTIONAL_MODULE([asf], true)
 AC_LMS_OPTIONAL_MODULE([rm], true)
 AC_LMS_OPTIONAL_MODULE([mp4], true, [CHECK_MODULE_MP4])
-AC_LMS_OPTIONAL_MODULE([aac], true)
+AC_LMS_OPTIONAL_MODULE([id3], true)
 AC_LMS_OPTIONAL_MODULE([flac], true, [CHECK_MODULE_FLAC])
 
 AC_OUTPUT([
@@ -93,7 +85,6 @@ src/plugins/Makefile
 src/plugins/dummy/Makefile
 src/plugins/jpeg/Makefile
 src/plugins/png/Makefile
-src/plugins/id3lib/Makefile
 src/plugins/video-dummy/Makefile
 src/plugins/audio-dummy/Makefile
 src/plugins/m3u/Makefile
@@ -102,7 +93,7 @@ src/plugins/pls/Makefile
 src/plugins/asf/Makefile
 src/plugins/rm/Makefile
 src/plugins/mp4/Makefile
-src/plugins/aac/Makefile
+src/plugins/id3/Makefile
 src/plugins/flac/Makefile
 ])
 
index 61e7230..bb22f06 100644 (file)
@@ -14,10 +14,6 @@ if USE_MODULE_PNG
 SUBDIRS += png
 endif
 
-if USE_MODULE_ID3LIB
-SUBDIRS += id3lib
-endif
-
 if USE_MODULE_VIDEO_DUMMY
 SUBDIRS += video-dummy
 endif
@@ -50,8 +46,8 @@ if USE_MODULE_MP4
 SUBDIRS += mp4
 endif
 
-if USE_MODULE_AAC
-SUBDIRS += aac
+if USE_MODULE_ID3
+SUBDIRS += id3
 endif
 
 if USE_MODULE_FLAC
@@ -62,7 +58,6 @@ DIST_SUBDIRS = \
        dummy \
        jpeg \
        png \
-       id3lib \
        video-dummy \
        audio-dummy \
        m3u \
@@ -71,5 +66,5 @@ DIST_SUBDIRS = \
        asf \
        rm \
        mp4 \
-       aac \
+       id3 \
        flac
diff --git a/lightmediascanner/src/plugins/aac/Makefile.am b/lightmediascanner/src/plugins/aac/Makefile.am
deleted file mode 100644 (file)
index 6c1151d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-MAINTAINERCLEANFILES = Makefile.in
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_srcdir)/src/plugins/aac
-
-pkgdir = $(pluginsdir)
-pkg_LTLIBRARIES = aac.la
-aac_la_SOURCES = aac.c
-aac_la_DEPENDENCIES = $(top_builddir)/config.h
-aac_la_LIBADD = $(top_builddir)/src/lib/liblightmediascanner.la
-aac_la_LDFLAGS = -module -avoid-version
diff --git a/lightmediascanner/src/plugins/aac/aac.c b/lightmediascanner/src/plugins/aac/aac.c
deleted file mode 100644 (file)
index fb2f866..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-/**
- * Copyright (C) 2008 by INdT
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
- */
-
-/**
- * @brief
- *
- * aac file parser.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define _XOPEN_SOURCE 600
-#include <lightmediascanner_plugin.h>
-#include <lightmediascanner_db.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define ID3V2_GET_FRAME_INFO(frame_data, frame_size, str, len) { \
-    str = malloc(sizeof(char) * (frame_size + 1)); \
-    memcpy(str, frame_data, frame_size); \
-    str[frame_size] = '\0'; \
-    len = frame_size; \
-}
-
-#define ID3V2_HEADER_SIZE       10
-#define ID3V2_FOOTER_SIZE       10
-#define ID3V2_FRAME_HEADER_SIZE 10
-
-#define ID3V1_NUM_GENRES 126
-
-static const char *id3v1_genres[126] = {
-    "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
-    "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop",
-    "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative",
-    "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient",
-    "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
-    "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel",
-    "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative",
-    "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
-    "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance",
-    "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
-    "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
-    "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
-    "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll",
-    "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion",
-    "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
-    "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
-    "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
-    "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
-    "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore",
-    "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock",
-    "Drum Solo", "A capella", "Euro-House", "Dance Hall",
-};
-
-struct id3v2_frame_header {
-    char frame_id[4];
-    unsigned int frame_size;
-    int compression;
-    int data_length_indicator;
-};
-
-struct id3v1_tag {
-    char title[30];
-    char artist[30];
-    char album[30];
-    char year[4];
-    char comments[30];
-    char genre;
-} __attribute__((packed));
-
-struct plugin {
-    struct lms_plugin plugin;
-    lms_db_audio_t *audio_db;
-};
-
-static const char _name[] = "aac";
-static const struct lms_string_size _exts[] = {
-    LMS_STATIC_STRING_SIZE(".aac")
-};
-
-static int
-_is_id3v2_second_synch_byte(unsigned char byte)
-{
-    if (byte == 0xff)
-        return 0;
-    if ((byte & 0xE0) == 0xE0)
-        return 1;
-    return 0;
-}
-
-static long
-_find_id3v2(int fd)
-{
-    long buffer_offset = 0;
-    char buffer[1025];
-    char *p;
-    int buffer_size = sizeof(buffer);
-    const char pattern[] = "ID3";
-    ssize_t nread;
-
-    /* These variables are used to keep track of a partial match that happens at
-     * the end of a buffer. */
-    int previous_partial_match = -1;
-    int previous_partial_synch_match = 0;
-    int first_synch_byte;
-
-    /* Start the search at the beginning of the file. */
-    lseek(fd, 0, SEEK_SET);
-
-    /* This loop is the crux of the find method.  There are three cases that we
-     * want to account for:
-     * (1) The previously searched buffer contained a partial match of the search
-     * pattern and we want to see if the next one starts with the remainder of
-     * that pattern.
-     *
-     * (2) The search pattern is wholly contained within the current buffer.
-     *
-     * (3) The current buffer ends with a partial match of the pattern.  We will
-     * note this for use in the next iteration, where we will check for the rest
-     * of the pattern. */
-
-    if ((nread = read(fd, &buffer, sizeof(buffer))) <= 0)
-        return -1;
-    while (1) {
-        /* (1) previous partial match */
-        if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
-            return -1;
-
-        if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
-            const int pattern_offset = buffer_size - previous_partial_match;
-
-            if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
-                return buffer_offset - buffer_size + previous_partial_match;
-        }
-
-        /* (2) pattern contained in current buffer */
-        p = buffer;
-        while ((p = memchr(p, 'I', buffer_size))) {
-            if (memcmp(p, pattern, 3) == 0)
-                return buffer_offset + (p - buffer);
-            p += 1;
-        }
-
-        p = memchr(buffer, 255, buffer_size);
-        if (p)
-            first_synch_byte = p - buffer;
-        else
-            first_synch_byte = -1;
-
-        /* Here we have to loop because there could be several of the first
-         * (11111111) byte, and we want to check all such instances until we find
-         * a full match (11111111 111) or hit the end of the buffer. */
-        while (first_synch_byte >= 0) {
-            /* if this *is not* at the end of the buffer */
-            if (first_synch_byte < buffer_size - 1) {
-                if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
-                    /* We've found the frame synch pattern. */
-                    return -1;
-                else
-                    /* We found 11111111 at the end of the current buffer indicating a
-                     * partial match of the synch pattern.  The find() below should
-                     * return -1 and break out of the loop. */
-                    previous_partial_synch_match = 1;
-            }
-
-            /* Check in the rest of the buffer. */
-            p = memchr(p + 1, 255, buffer_size);
-            if (p)
-                first_synch_byte = p - buffer;
-            else
-                first_synch_byte = -1;
-        }
-
-        /* (3) partial match */
-        if (buffer[nread - 1] == pattern[1])
-            previous_partial_match = nread - 1;
-        else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
-            previous_partial_match = nread - 2;
-        buffer_offset += buffer_size;
-
-        if ((nread = read(fd, &buffer, sizeof(buffer))) <= 0)
-            return -1;
-    }
-
-    return -1;
-}
-
-static unsigned int
-_to_uint(const char *data, int data_size)
-{
-    unsigned int sum = 0;
-    int last = data_size > 4 ? 3 : data_size - 1;
-    int i;
-
-    for (i = 0; i <= last; i++)
-        sum |= (data[i] & 0x7f) << ((last - i) * 7);
-
-    return sum;
-}
-
-static unsigned int
-_get_id3v2_frame_header_size(unsigned int version)
-{
-    switch (version) {
-    case 0:
-    case 1:
-    case 2:
-        return 6;
-    case 3:
-    case 4:
-    default:
-        return 10;
-    }
-}
-
-static void
-_parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
-{
-    switch (version) {
-    case 0:
-    case 1:
-    case 2:
-        memcpy(fh->frame_id, data, 3);
-        fh->frame_id[3] = 0;
-        fh->frame_size = _to_uint(data + 3, 3);
-        fh->compression = 0;
-        fh->data_length_indicator = 0;
-        break;
-    case 3:
-        memcpy(fh->frame_id, data, 4);
-        fh->frame_size = _to_uint(data + 4, 4);
-        fh->compression = data[9] & 0x40;
-        fh->data_length_indicator = 0;
-        break;
-    case 4:
-    default:
-        memcpy(fh->frame_id, data, 4);
-        fh->frame_size = _to_uint(data + 4, 4);
-        fh->compression = data[9] & 0x4;
-        fh->data_length_indicator = data[9] & 0x1;
-        break;
-    }
-}
-
-static void
-_parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct lms_audio_info *info)
-{
-    unsigned int frame_size;
-
-    /* TODO proper handle text encoding
-     *
-     * Latin1  = 0
-     * UTF16   = 1
-     * UTF16BE = 2
-     * UTF8    = 3
-     * UTF16LE = 4
-     */
-
-    /* skip first byte - text encoding */
-    frame_data += 1;
-    frame_size = fh->frame_size - 1;
-
-    if (memcmp(fh->frame_id, "TIT2", 4) == 0)
-        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->title.str, info->title.len)
-    else if (memcmp(fh->frame_id, "TPE1", 4) == 0)
-        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->artist.str, info->artist.len)
-    else if (memcmp(fh->frame_id, "TALB", 4) == 0)
-        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->album.str, info->album.len)
-    else if (memcmp(fh->frame_id, "TRCK", 4) == 0) {
-        char *trackno;
-        unsigned int trackno_len;
-        ID3V2_GET_FRAME_INFO(frame_data, frame_size, trackno, trackno_len);
-        info->trackno = atoi(trackno);
-    }
-}
-
-static int
-_parse_id3v2(int fd, long id3v2_offset, struct lms_audio_info *info)
-{
-    char header_data[10], frame_header_data[10];
-    unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
-    int extended_header, footer_present;
-    struct id3v2_frame_header fh;
-
-    lseek(fd, id3v2_offset, SEEK_SET);
-
-    /* parse header */
-    if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
-        return -1;
-
-    tag_size = _to_uint(header_data + 6, 4);
-    if (tag_size == 0)
-        return -1;
-
-    /* parse frames */
-    major_version = header_data[3];
-
-    frame_data_pos = 0;
-    frame_data_length = tag_size;
-
-    /* check for extended header */
-    extended_header = header_data[5] & 0x20; /* bit 6 */
-    if (extended_header) {
-        /* skip extended header */
-        unsigned int extended_header_size;
-        char extended_header_data[4];
-
-        if (read(fd, extended_header_data, 4) != 4)
-            return -1;
-        extended_header_size = _to_uint(extended_header_data, 4);
-        lseek(fd, extended_header_size - 4, SEEK_CUR);
-        frame_data_pos += extended_header_size;
-        frame_data_length -= extended_header_size;
-    }
-
-    footer_present = header_data[5] & 0x8;   /* bit 4 */
-    if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
-        frame_data_length -= ID3V2_FOOTER_SIZE;
-
-    frame_header_size = _get_id3v2_frame_header_size(major_version);
-    while (frame_data_pos < frame_data_length - frame_header_size) {
-        if (read(fd, frame_header_data, ID3V2_FRAME_HEADER_SIZE) != ID3V2_FRAME_HEADER_SIZE)
-            return -1;
-
-        if (frame_header_data[0] == 0)
-            break;
-
-        _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
-
-        if (!fh.compression &&
-            fh.frame_id[0] == 'T' &&
-            memcmp(fh.frame_id, "TXXX", 4) != 0) {
-            char *frame_data;
-
-            if (fh.data_length_indicator)
-                lseek(fd, 4, SEEK_CUR);
-
-            frame_data = malloc(sizeof(char) * fh.frame_size);
-            if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
-                free(frame_data);
-                return -1;
-            }
-
-            _parse_id3v2_frame(&fh, frame_data, info);
-            free(frame_data);
-        }
-        else {
-            if (fh.data_length_indicator)
-                lseek(fd, fh.frame_size + 4, SEEK_CUR);
-            else
-                lseek(fd, fh.frame_size, SEEK_CUR);
-        }
-
-        frame_data_pos += fh.frame_size + frame_header_size;
-    }
-
-    return 0;
-}
-
-static int
-_parse_id3v1(int fd, struct lms_audio_info *info)
-{
-    struct id3v1_tag tag;
-    if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
-        return -1;
-
-    info->title.str = strdup(tag.title);
-    info->title.len = strlen(info->title.str);
-    info->artist.str = strdup(tag.artist);
-    info->artist.len = strlen(info->artist.str);
-    info->album.str = strdup(tag.album);
-    info->album.len = strlen(info->album.str);
-    if (tag.genre >= 0 && tag.genre < ID3V1_NUM_GENRES) {
-        info->genre.str = strdup(id3v1_genres[(int) tag.genre]);
-        info->genre.len = strlen(info->genre.str);
-    }
-
-    return 0;
-}
-
-static void *
-_match(struct plugin *p, const char *path, int len, int base)
-{
-    int i;
-
-    i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
-    if (i < 0)
-      return NULL;
-    else
-      return (void*)(i + 1);
-}
-
-static int
-_parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
-{
-    struct lms_audio_info info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
-    int r, fd;
-    long id3v2_offset;
-
-    fd = open(finfo->path, O_RDONLY);
-    if (fd < 0) {
-        perror("open");
-        return -1;
-    }
-
-    id3v2_offset = _find_id3v2(fd);
-    if (id3v2_offset >= 0) {
-        fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
-                finfo->path, id3v2_offset);
-        if (_parse_id3v2(fd, id3v2_offset, &info) != 0) {
-            r = -2;
-            goto done;
-        }
-    }
-    else {
-        char tag[3];
-
-        fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
-        /* check for id3v1 tag */
-        if (lseek(fd, -128, SEEK_END) == -1) {
-            r = -3;
-            goto done;
-        }
-
-        if (read(fd, &tag, 3) == -1) {
-            r = -4;
-            goto done;
-        }
-
-        if (memcmp(tag, "TAG", 3) == 0) {
-            fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
-            if (_parse_id3v1(fd, &info) != 0) {
-                r = -5;
-                goto done;
-            }
-        }
-    }
-
-    lms_string_size_strip_and_free(&info.title);
-    lms_string_size_strip_and_free(&info.artist);
-    lms_string_size_strip_and_free(&info.album);
-    lms_string_size_strip_and_free(&info.genre);
-
-    if (!info.title.str) {
-        int ext_idx;
-        ext_idx = ((int)match) - 1;
-        info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
-        info.title.str = malloc((info.title.len + 1) * sizeof(char));
-        memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
-        info.title.str[info.title.len] = '\0';
-    }
-    lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
-
-    if (info.artist.str)
-        lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
-    if (info.album.str)
-        lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
-    if (info.genre.str)
-        lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
-
-#if 0
-    fprintf(stderr, "file %s info\n", finfo->path);
-    fprintf(stderr, "\ttitle='%s'\n", info.title.str);
-    fprintf(stderr, "\tartist='%s'\n", info.artist.str);
-    fprintf(stderr, "\talbum='%s'\n", info.album.str);
-    fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
-#endif
-
-    info.id = finfo->id;
-    r = lms_db_audio_add(plugin->audio_db, &info);
-
-  done:
-    close(fd);
-
-    if (info.title.str)
-        free(info.title.str);
-    if (info.artist.str)
-        free(info.artist.str);
-    if (info.album.str)
-        free(info.album.str);
-    if (info.genre.str)
-        free(info.genre.str);
-
-    return r;
-}
-
-static int
-_setup(struct plugin *plugin, struct lms_context *ctxt)
-{
-    plugin->audio_db = lms_db_audio_new(ctxt->db);
-    if (!plugin->audio_db)
-        return -1;
-    return 0;
-}
-
-static int
-_start(struct plugin *plugin, struct lms_context *ctxt)
-{
-    return lms_db_audio_start(plugin->audio_db);
-}
-
-static int
-_finish(struct plugin *plugin, struct lms_context *ctxt)
-{
-    if (plugin->audio_db)
-        lms_db_audio_free(plugin->audio_db);
-    return 0;
-}
-
-static int
-_close(struct plugin *plugin)
-{
-    free(plugin);
-    return 0;
-}
-
-API struct lms_plugin *
-lms_plugin_open(void)
-{
-    struct plugin *plugin;
-
-    plugin = (struct plugin *)malloc(sizeof(*plugin));
-    plugin->plugin.name = _name;
-    plugin->plugin.match = (lms_plugin_match_fn_t)_match;
-    plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
-    plugin->plugin.close = (lms_plugin_close_fn_t)_close;
-    plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
-    plugin->plugin.start = (lms_plugin_start_fn_t)_start;
-    plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
-
-    return (struct lms_plugin *)plugin;
-}
diff --git a/lightmediascanner/src/plugins/flac/Makefile.am b/lightmediascanner/src/plugins/flac/Makefile.am
new file mode 100644 (file)
index 0000000..6c3d568
--- /dev/null
@@ -0,0 +1,10 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_srcdir)/src/plugins/flac @FLAC_CFLAGS@
+
+pkgdir = $(pluginsdir)
+pkg_LTLIBRARIES = flac.la
+flac_la_SOURCES = flac.c
+flac_la_DEPENDENCIES = $(top_builddir)/config.h
+flac_la_LIBADD = $(top_builddir)/src/lib/liblightmediascanner.la @FLAC_LIBS@
+flac_la_LDFLAGS = -module -avoid-version
diff --git a/lightmediascanner/src/plugins/id3/Makefile.am b/lightmediascanner/src/plugins/id3/Makefile.am
new file mode 100644 (file)
index 0000000..ede48f5
--- /dev/null
@@ -0,0 +1,10 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_srcdir)/src/plugins/id3
+
+pkgdir = $(pluginsdir)
+pkg_LTLIBRARIES = id3.la
+id3_la_SOURCES = id3.c
+id3_la_DEPENDENCIES = $(top_builddir)/config.h
+id3_la_LIBADD = $(top_builddir)/src/lib/liblightmediascanner.la
+id3_la_LDFLAGS = -module -avoid-version
diff --git a/lightmediascanner/src/plugins/id3/id3.c b/lightmediascanner/src/plugins/id3/id3.c
new file mode 100644 (file)
index 0000000..8aeaac9
--- /dev/null
@@ -0,0 +1,719 @@
+/**
+ * Copyright (C) 2008 by INdT
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
+ */
+
+/**
+ * @brief
+ *
+ * id3 file parser.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define _XOPEN_SOURCE 600
+#include <lightmediascanner_plugin.h>
+#include <lightmediascanner_db.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ID3V2_GET_FRAME_INFO(frame_data, frame_size, str, len) { \
+    str = malloc(sizeof(char) * (frame_size + 1)); \
+    memcpy(str, frame_data, frame_size); \
+    str[frame_size] = '\0'; \
+    len = frame_size; \
+}
+
+#define ID3V2_HEADER_SIZE       10
+#define ID3V2_FOOTER_SIZE       10
+#define ID3V2_FRAME_HEADER_SIZE 10
+
+#define ID3V1_NUM_GENRES 148
+
+static const char *id3v1_genres[ID3V1_NUM_GENRES] = {
+    "Blues",
+    "Classic Rock",
+    "Country",
+    "Dance",
+    "Disco",
+    "Funk",
+    "Grunge",
+    "Hip-Hop",
+    "Jazz",
+    "Metal",
+    "New Age",
+    "Oldies",
+    "Other",
+    "Pop",
+    "R&B",
+    "Rap",
+    "Reggae",
+    "Rock",
+    "Techno",
+    "Industrial",
+    "Alternative",
+    "Ska",
+    "Death Metal",
+    "Pranks",
+    "Soundtrack",
+    "Euro-Techno",
+    "Ambient",
+    "Trip-Hop",
+    "Vocal",
+    "Jazz+Funk",
+    "Fusion",
+    "Trance",
+    "Classical",
+    "Instrumental",
+    "Acid",
+    "House",
+    "Game",
+    "Sound Clip",
+    "Gospel",
+    "Noise",
+    "Alternative Rock",
+    "Bass",
+    "Soul",
+    "Punk",
+    "Space",
+    "Meditative",
+    "Instrumental Pop",
+    "Instrumental Rock",
+    "Ethnic",
+    "Gothic",
+    "Darkwave",
+    "Techno-Industrial",
+    "Electronic",
+    "Pop-Folk",
+    "Eurodance",
+    "Dream",
+    "Southern Rock",
+    "Comedy",
+    "Cult",
+    "Gangsta",
+    "Top 40",
+    "Christian Rap",
+    "Pop/Funk",
+    "Jungle",
+    "Native American",
+    "Cabaret",
+    "New Wave",
+    "Psychedelic",
+    "Rave",
+    "Showtunes",
+    "Trailer",
+    "Lo-Fi",
+    "Tribal",
+    "Acid Punk",
+    "Acid Jazz",
+    "Polka",
+    "Retro",
+    "Musical",
+    "Rock & Roll",
+    "Hard Rock",
+    "Folk",
+    "Folk/Rock",
+    "National Folk",
+    "Swing",
+    "Fusion",
+    "Bebob",
+    "Latin",
+    "Revival",
+    "Celtic",
+    "Bluegrass",
+    "Avantgarde",
+    "Gothic Rock",
+    "Progressive Rock",
+    "Psychedelic Rock",
+    "Symphonic Rock",
+    "Slow Rock",
+    "Big Band",
+    "Chorus",
+    "Easy Listening",
+    "Acoustic",
+    "Humour",
+    "Speech",
+    "Chanson",
+    "Opera",
+    "Chamber Music",
+    "Sonata",
+    "Symphony",
+    "Booty Bass",
+    "Primus",
+    "Porn Groove",
+    "Satire",
+    "Slow Jam",
+    "Club",
+    "Tango",
+    "Samba",
+    "Folklore",
+    "Ballad",
+    "Power Ballad",
+    "Rhythmic Soul",
+    "Freestyle",
+    "Duet",
+    "Punk Rock",
+    "Drum Solo",
+    "A Cappella",
+    "Euro-House",
+    "Dance Hall",
+    "Goa",
+    "Drum & Bass",
+    "Club-House",
+    "Hardcore",
+    "Terror",
+    "Indie",
+    "BritPop",
+    "Negerpunk",
+    "Polsk Punk",
+    "Beat",
+    "Christian Gangsta Rap",
+    "Heavy Metal",
+    "Black Metal",
+    "Crossover",
+    "Contemporary Christian",
+    "Christian Rock",
+    "Merengue",
+    "Salsa",
+    "Thrash Metal",
+    "Anime",
+    "Jpop",
+    "Synthpop"
+};
+
+struct id3v2_frame_header {
+    char frame_id[4];
+    unsigned int frame_size;
+    int compression;
+    int data_length_indicator;
+};
+
+struct id3v1_tag {
+    char title[30];
+    char artist[30];
+    char album[30];
+    char year[4];
+    char comments[30];
+    char genre;
+} __attribute__((packed));
+
+struct plugin {
+    struct lms_plugin plugin;
+    lms_db_audio_t *audio_db;
+};
+
+static const char _name[] = "id3";
+static const struct lms_string_size _exts[] = {
+    LMS_STATIC_STRING_SIZE(".mp3"),
+    LMS_STATIC_STRING_SIZE(".aac")
+};
+
+static int
+_is_id3v2_second_synch_byte(unsigned char byte)
+{
+    if (byte == 0xff)
+        return 0;
+    if ((byte & 0xE0) == 0xE0)
+        return 1;
+    return 0;
+}
+
+static long
+_find_id3v2(int fd)
+{
+    long buffer_offset = 0;
+    char buffer[1025];
+    char *p;
+    int buffer_size = sizeof(buffer);
+    const char pattern[] = "ID3";
+    ssize_t nread;
+
+    /* These variables are used to keep track of a partial match that happens at
+     * the end of a buffer. */
+    int previous_partial_match = -1;
+    int previous_partial_synch_match = 0;
+    int first_synch_byte;
+
+    /* Start the search at the beginning of the file. */
+    lseek(fd, 0, SEEK_SET);
+
+    /* This loop is the crux of the find method.  There are three cases that we
+     * want to account for:
+     * (1) The previously searched buffer contained a partial match of the search
+     * pattern and we want to see if the next one starts with the remainder of
+     * that pattern.
+     *
+     * (2) The search pattern is wholly contained within the current buffer.
+     *
+     * (3) The current buffer ends with a partial match of the pattern.  We will
+     * note this for use in the next iteration, where we will check for the rest
+     * of the pattern. */
+
+    if ((nread = read(fd, &buffer, sizeof(buffer))) <= 0)
+        return -1;
+    while (1) {
+        /* (1) previous partial match */
+        if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
+            return -1;
+
+        if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
+            const int pattern_offset = buffer_size - previous_partial_match;
+
+            if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
+                return buffer_offset - buffer_size + previous_partial_match;
+        }
+
+        /* (2) pattern contained in current buffer */
+        p = buffer;
+        while ((p = memchr(p, 'I', buffer_size))) {
+            if (memcmp(p, pattern, 3) == 0)
+                return buffer_offset + (p - buffer);
+            p += 1;
+        }
+
+        p = memchr(buffer, 255, buffer_size);
+        if (p)
+            first_synch_byte = p - buffer;
+        else
+            first_synch_byte = -1;
+
+        /* Here we have to loop because there could be several of the first
+         * (11111111) byte, and we want to check all such instances until we find
+         * a full match (11111111 111) or hit the end of the buffer. */
+        while (first_synch_byte >= 0) {
+            /* if this *is not* at the end of the buffer */
+            if (first_synch_byte < buffer_size - 1) {
+                if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
+                    /* We've found the frame synch pattern. */
+                    return -1;
+                else
+                    /* We found 11111111 at the end of the current buffer indicating a
+                     * partial match of the synch pattern.  The find() below should
+                     * return -1 and break out of the loop. */
+                    previous_partial_synch_match = 1;
+            }
+
+            /* Check in the rest of the buffer. */
+            p = memchr(p + 1, 255, buffer_size);
+            if (p)
+                first_synch_byte = p - buffer;
+            else
+                first_synch_byte = -1;
+        }
+
+        /* (3) partial match */
+        if (buffer[nread - 1] == pattern[1])
+            previous_partial_match = nread - 1;
+        else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
+            previous_partial_match = nread - 2;
+        buffer_offset += buffer_size;
+
+        if ((nread = read(fd, &buffer, sizeof(buffer))) <= 0)
+            return -1;
+    }
+
+    return -1;
+}
+
+static unsigned int
+_to_uint(const char *data, int data_size)
+{
+    unsigned int sum = 0;
+    int last = data_size > 4 ? 3 : data_size - 1;
+    int i;
+
+    for (i = 0; i <= last; i++)
+        sum |= (data[i] & 0x7f) << ((last - i) * 7);
+
+    return sum;
+}
+
+static unsigned int
+_get_id3v2_frame_header_size(unsigned int version)
+{
+    switch (version) {
+    case 0:
+    case 1:
+    case 2:
+        return 6;
+    case 3:
+    case 4:
+    default:
+        return 10;
+    }
+}
+
+static void
+_parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
+{
+    switch (version) {
+    case 0:
+    case 1:
+    case 2:
+        memcpy(fh->frame_id, data, 3);
+        fh->frame_id[3] = 0;
+        fh->frame_size = _to_uint(data + 3, 3);
+        fh->compression = 0;
+        fh->data_length_indicator = 0;
+        break;
+    case 3:
+        memcpy(fh->frame_id, data, 4);
+        fh->frame_size = _to_uint(data + 4, 4);
+        fh->compression = data[9] & 0x40;
+        fh->data_length_indicator = 0;
+        break;
+    case 4:
+    default:
+        memcpy(fh->frame_id, data, 4);
+        fh->frame_size = _to_uint(data + 4, 4);
+        fh->compression = data[9] & 0x4;
+        fh->data_length_indicator = data[9] & 0x1;
+        break;
+    }
+}
+
+static void
+_parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct lms_audio_info *info)
+{
+    unsigned int frame_size;
+
+    /* TODO proper handle text encoding
+     *
+     * Latin1  = 0
+     * UTF16   = 1
+     * UTF16BE = 2
+     * UTF8    = 3
+     * UTF16LE = 4
+     */
+
+    /* skip first byte - text encoding */
+    frame_data += 1;
+    frame_size = fh->frame_size - 1;
+
+    if (memcmp(fh->frame_id, "TIT2", 4) == 0)
+        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->title.str, info->title.len)
+    else if (memcmp(fh->frame_id, "TPE1", 4) == 0)
+        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->artist.str, info->artist.len)
+    else if (memcmp(fh->frame_id, "TALB", 4) == 0)
+        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->album.str, info->album.len)
+    else if (memcmp(fh->frame_id, "TCON", 4) == 0) {
+        /* TODO handle multiple genres */
+        int i, is_number;
+
+        ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->genre.str, info->genre.len)
+
+        is_number = 1;
+        for (i = 0; i < info->genre.len; ++i) {
+            if (!isdigit(info->genre.str[i]))
+                is_number = 0;
+        }
+
+        if ((is_number) &&
+            (info->genre.str) &&
+            (info->genre.len > 0)) {
+            int genre = atoi(info->genre.str);
+            if (genre >= 0 && genre < ID3V1_NUM_GENRES) {
+                free(info->genre.str);
+                info->genre.str = strdup(id3v1_genres[(int) genre]);
+                info->genre.len = strlen(info->genre.str);
+            }
+            else {
+                /* ignore other genres */
+                free(info->genre.str);
+                info->genre.str = NULL;
+                info->genre.len = 0;
+            }
+        }
+    }
+    else if (memcmp(fh->frame_id, "TRCK", 4) == 0) {
+        char *trackno;
+        unsigned int trackno_len;
+        ID3V2_GET_FRAME_INFO(frame_data, frame_size, trackno, trackno_len);
+        info->trackno = atoi(trackno);
+    }
+}
+
+static int
+_parse_id3v2(int fd, long id3v2_offset, struct lms_audio_info *info)
+{
+    char header_data[10], frame_header_data[10];
+    unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
+    int extended_header, footer_present;
+    struct id3v2_frame_header fh;
+
+    lseek(fd, id3v2_offset, SEEK_SET);
+
+    /* parse header */
+    if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
+        return -1;
+
+    tag_size = _to_uint(header_data + 6, 4);
+    if (tag_size == 0)
+        return -1;
+
+    /* parse frames */
+    major_version = header_data[3];
+
+    frame_data_pos = 0;
+    frame_data_length = tag_size;
+
+    /* check for extended header */
+    extended_header = header_data[5] & 0x20; /* bit 6 */
+    if (extended_header) {
+        /* skip extended header */
+        unsigned int extended_header_size;
+        char extended_header_data[4];
+
+        if (read(fd, extended_header_data, 4) != 4)
+            return -1;
+        extended_header_size = _to_uint(extended_header_data, 4);
+        lseek(fd, extended_header_size - 4, SEEK_CUR);
+        frame_data_pos += extended_header_size;
+        frame_data_length -= extended_header_size;
+    }
+
+    footer_present = header_data[5] & 0x8;   /* bit 4 */
+    if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
+        frame_data_length -= ID3V2_FOOTER_SIZE;
+
+    frame_header_size = _get_id3v2_frame_header_size(major_version);
+    while (frame_data_pos < frame_data_length - frame_header_size) {
+        if (read(fd, frame_header_data, ID3V2_FRAME_HEADER_SIZE) != ID3V2_FRAME_HEADER_SIZE)
+            return -1;
+
+        if (frame_header_data[0] == 0)
+            break;
+
+        _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
+
+        if (!fh.compression &&
+            fh.frame_id[0] == 'T' &&
+            memcmp(fh.frame_id, "TXXX", 4) != 0) {
+            char *frame_data;
+
+            if (fh.data_length_indicator)
+                lseek(fd, 4, SEEK_CUR);
+
+            frame_data = malloc(sizeof(char) * fh.frame_size);
+            if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
+                free(frame_data);
+                return -1;
+            }
+
+            _parse_id3v2_frame(&fh, frame_data, info);
+            free(frame_data);
+        }
+        else {
+            if (fh.data_length_indicator)
+                lseek(fd, fh.frame_size + 4, SEEK_CUR);
+            else
+                lseek(fd, fh.frame_size, SEEK_CUR);
+        }
+
+        frame_data_pos += fh.frame_size + frame_header_size;
+    }
+
+    return 0;
+}
+
+static int
+_parse_id3v1(int fd, struct lms_audio_info *info)
+{
+    struct id3v1_tag tag;
+    if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
+        return -1;
+
+    info->title.str = strdup(tag.title);
+    info->title.len = strlen(info->title.str);
+    info->artist.str = strdup(tag.artist);
+    info->artist.len = strlen(info->artist.str);
+    info->album.str = strdup(tag.album);
+    info->album.len = strlen(info->album.str);
+    info->genre.str = strdup(id3v1_genres[(int) tag.genre]);
+    info->genre.len = strlen(info->genre.str);
+    if (tag.comments[28] == 0 && tag.comments[29] != 0)
+        info->trackno = (unsigned char) tag.comments[29];
+
+    return 0;
+}
+
+static void *
+_match(struct plugin *p, const char *path, int len, int base)
+{
+    int i;
+
+    i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
+    if (i < 0)
+      return NULL;
+    else
+      return (void*)(i + 1);
+}
+
+static int
+_parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
+{
+    struct lms_audio_info info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
+    int r, fd;
+    long id3v2_offset;
+
+    fd = open(finfo->path, O_RDONLY);
+    if (fd < 0) {
+        perror("open");
+        return -1;
+    }
+
+    id3v2_offset = _find_id3v2(fd);
+    if (id3v2_offset >= 0) {
+#if 0
+        fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
+                finfo->path, id3v2_offset);
+#endif
+        if (_parse_id3v2(fd, id3v2_offset, &info) != 0) {
+            r = -2;
+            goto done;
+        }
+    }
+    else {
+        char tag[3];
+#if 0
+        fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
+#endif
+        /* check for id3v1 tag */
+        if (lseek(fd, -128, SEEK_END) == -1) {
+            r = -3;
+            goto done;
+        }
+
+        if (read(fd, &tag, 3) == -1) {
+            r = -4;
+            goto done;
+        }
+
+        if (memcmp(tag, "TAG", 3) == 0) {
+#if 0
+            fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
+#endif
+            if (_parse_id3v1(fd, &info) != 0) {
+                r = -5;
+                goto done;
+            }
+        }
+    }
+
+    lms_string_size_strip_and_free(&info.title);
+    lms_string_size_strip_and_free(&info.artist);
+    lms_string_size_strip_and_free(&info.album);
+    lms_string_size_strip_and_free(&info.genre);
+
+    if (!info.title.str) {
+        int ext_idx;
+        ext_idx = ((int)match) - 1;
+        info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
+        info.title.str = malloc((info.title.len + 1) * sizeof(char));
+        memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
+        info.title.str[info.title.len] = '\0';
+    }
+    lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
+
+    if (info.artist.str)
+        lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
+    if (info.album.str)
+        lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
+    if (info.genre.str)
+        lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
+
+#if 0
+    fprintf(stderr, "file %s info\n", finfo->path);
+    fprintf(stderr, "\ttitle='%s'\n", info.title.str);
+    fprintf(stderr, "\tartist='%s'\n", info.artist.str);
+    fprintf(stderr, "\talbum='%s'\n", info.album.str);
+    fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
+    fprintf(stderr, "\ttrack number='%d'\n", info.trackno);
+#endif
+
+    info.id = finfo->id;
+    r = lms_db_audio_add(plugin->audio_db, &info);
+
+  done:
+    close(fd);
+
+    if (info.title.str)
+        free(info.title.str);
+    if (info.artist.str)
+        free(info.artist.str);
+    if (info.album.str)
+        free(info.album.str);
+    if (info.genre.str)
+        free(info.genre.str);
+
+    return r;
+}
+
+static int
+_setup(struct plugin *plugin, struct lms_context *ctxt)
+{
+    plugin->audio_db = lms_db_audio_new(ctxt->db);
+    if (!plugin->audio_db)
+        return -1;
+    return 0;
+}
+
+static int
+_start(struct plugin *plugin, struct lms_context *ctxt)
+{
+    return lms_db_audio_start(plugin->audio_db);
+}
+
+static int
+_finish(struct plugin *plugin, struct lms_context *ctxt)
+{
+    if (plugin->audio_db)
+        lms_db_audio_free(plugin->audio_db);
+    return 0;
+}
+
+static int
+_close(struct plugin *plugin)
+{
+    free(plugin);
+    return 0;
+}
+
+API struct lms_plugin *
+lms_plugin_open(void)
+{
+    struct plugin *plugin;
+
+    plugin = (struct plugin *)malloc(sizeof(*plugin));
+    plugin->plugin.name = _name;
+    plugin->plugin.match = (lms_plugin_match_fn_t)_match;
+    plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
+    plugin->plugin.close = (lms_plugin_close_fn_t)_close;
+    plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
+    plugin->plugin.start = (lms_plugin_start_fn_t)_start;
+    plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
+
+    return (struct lms_plugin *)plugin;
+}
diff --git a/lightmediascanner/src/plugins/id3lib/Makefile.am b/lightmediascanner/src/plugins/id3lib/Makefile.am
deleted file mode 100644 (file)
index 4f9dd4f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-MAINTAINERCLEANFILES = Makefile.in
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_srcdir)/src/plugins/id3lib @ID3LIB_CFLAGS@
-
-pkgdir = $(pluginsdir)
-pkg_LTLIBRARIES = id3lib.la
-id3lib_la_SOURCES = id3lib.cpp
-id3lib_la_DEPENDENCIES = $(top_builddir)/config.h
-id3lib_la_LIBADD = $(top_builddir)/src/lib/liblightmediascanner.la @ID3LIB_LIBS@
-id3lib_la_LDFLAGS = -module -avoid-version
diff --git a/lightmediascanner/src/plugins/id3lib/id3lib.cpp b/lightmediascanner/src/plugins/id3lib/id3lib.cpp
deleted file mode 100644 (file)
index e7680e7..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-/**
- * Copyright (C) 2007 by INdT
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
- */
-
-/**
- * @brief
- *
- * Reads ID3 tags from MP3 using id3lib.
- *
- * @todo: write a faster module to replace this one, using no external libs.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define _XOPEN_SOURCE 600
-#include <lightmediascanner_plugin.h>
-#include <lightmediascanner_utils.h>
-#include <lightmediascanner_db.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <id3/tag.h>
-
-extern "C" {
-
-static void
-_id3lib_get_string(const ID3_Frame *frame, ID3_FieldID field_id, struct lms_string_size *s)
-{
-  ID3_Field* field;
-  ID3_TextEnc enc;
-  size_t size;
-
-  field = frame->GetField(field_id);
-  if (!field)
-    return;
-
-  enc = field->GetEncoding();
-  field->SetEncoding(ID3TE_ISO8859_1);
-  size = field->Size();
-  if (size < 1)
-    goto done;
-
-  size++;
-  s->str = (char *)malloc(size * sizeof(char));
-  s->len = field->Get(s->str, size);
-  lms_string_size_strip_and_free(s);
-
- done:
-  field->SetEncoding(enc);
-}
-
-static unsigned int
-_id3lib_get_string_as_int(const ID3_Frame *frame, ID3_FieldID field_id)
-{
-  char buf[32];
-  ID3_Field* field;
-  size_t size;
-
-  field = frame->GetField(field_id);
-  if (!field)
-    return 0;
-
-  size = field->Get(buf, sizeof(buf));
-  if (size > 0)
-    return atoi(buf);
-
-  return 0;
-}
-
-inline static int
-_id3lib_get_string_if_need(const ID3_Frame *frame, struct lms_string_size *s)
-{
-  if (!s->str)
-    _id3lib_get_string(frame, ID3FN_TEXT, s);
-
-  return !!s->str;
-}
-
-static int
-_id3lib_get_data(const ID3_Tag &tag, struct lms_audio_info *info)
-{
-  lms_string_size band = {NULL, 0}, lead_artist = {NULL, 0};
-  ID3_Tag::ConstIterator *itr;
-  const ID3_Frame *frame;
-  int todo;
-
-  todo = 8; /* info fields left to parse: title, lead artist, band,
-               album, genre, trackno, rating, playcnt */
-
-  itr = tag.CreateIterator();
-
-  while ((frame = itr->GetNext()) != NULL && todo > 0) {
-    ID3_FrameID fid;
-
-    fid = frame->GetID();
-
-    switch (fid) {
-    case ID3FID_TITLE:
-      if (_id3lib_get_string_if_need(frame, &info->title))
-          todo--;
-      break;
-
-    case ID3FID_LEADARTIST:
-      if (_id3lib_get_string_if_need(frame, &lead_artist))
-          todo--;
-      break;
-
-    case ID3FID_BAND:
-      if (_id3lib_get_string_if_need(frame, &band))
-          todo--;
-      break;
-
-    case ID3FID_ALBUM:
-      if (_id3lib_get_string_if_need(frame, &info->album))
-          todo--;
-      break;
-
-    case ID3FID_CONTENTTYPE:
-      if (_id3lib_get_string_if_need(frame, &info->genre))
-          todo--;
-      break;
-
-    case ID3FID_TRACKNUM:
-      if (!info->trackno) {
-        info->trackno = _id3lib_get_string_as_int(frame, ID3FN_TEXT);
-        if (info->trackno)
-          todo--;
-      }
-      break;
-
-    case ID3FID_POPULARIMETER:
-      if (!info->rating) {
-        info->rating = frame->GetField(ID3FN_RATING)->Get();
-        if (info->rating)
-          todo--;
-      }
-      if (!info->playcnt) {
-        info->playcnt = frame->GetField(ID3FN_COUNTER)->Get();
-        if (info->playcnt)
-          todo--;
-      }
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  if (band.str && lead_artist.str) {
-    info->artist = band;
-    free(lead_artist.str);
-  } else if (band.str)
-    info->artist = band;
-  else if (lead_artist.str)
-    info->artist = lead_artist;
-
-  delete itr;
-  return 0;
-}
-
-static const char _name[] = "id3lib";
-static const struct lms_string_size _exts[] = {
-    LMS_STATIC_STRING_SIZE(".mp3")
-};
-
-struct plugin {
-    struct lms_plugin plugin;
-    lms_db_audio_t *audio_db;
-};
-
-static void *
-_match(struct plugin *p, const char *path, int len, int base)
-{
-    int i;
-
-    i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
-    if (i < 0)
-      return NULL;
-    else
-      return (void*)(i + 1);
-}
-
-static int
-_parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
-{
-    struct lms_audio_info info = {0};
-    ID3_Tag tag;
-    int r;
-
-    tag.Link(finfo->path);
-    r = _id3lib_get_data(tag, &info);
-    if (r != 0)
-      goto done;
-
-    if (!info.title.str) {
-      int ext_idx;
-
-      ext_idx = ((int)match) - 1;
-      info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
-      info.title.str = (char *)malloc((info.title.len + 1) * sizeof(char));
-      memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
-      info.title.str[info.title.len] = '\0';
-    }
-
-    if (info.title.str)
-      lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
-    if (info.artist.str)
-      lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
-    if (info.album.str)
-      lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
-    if (info.genre.str)
-      lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
-
-    info.id = finfo->id;
-    r = lms_db_audio_add(plugin->audio_db, &info);
-
- done:
-    if (info.title.str)
-        free(info.title.str);
-    if (info.artist.str)
-        free(info.artist.str);
-    if (info.album.str)
-        free(info.album.str);
-    if (info.genre.str)
-        free(info.genre.str);
-
-    return r;
-}
-
-static int
-_setup(struct plugin *plugin, struct lms_context *ctxt)
-{
-    plugin->audio_db = lms_db_audio_new(ctxt->db);
-    if (!plugin->audio_db)
-        return -1;
-
-    return 0;
-}
-
-static int
-_start(struct plugin *plugin, struct lms_context *ctxt)
-{
-    return lms_db_audio_start(plugin->audio_db);
-}
-
-static int
-_finish(struct plugin *plugin,  struct lms_context *ctxt)
-{
-    if (plugin->audio_db)
-        return lms_db_audio_free(plugin->audio_db);
-
-    return 0;
-}
-
-static int
-_close(struct plugin *plugin)
-{
-    free(plugin);
-    return 0;
-}
-
-API struct lms_plugin *
-lms_plugin_open(void)
-{
-    struct plugin *plugin;
-
-    plugin = (struct plugin *)malloc(sizeof(*plugin));
-    plugin->plugin.name = _name;
-    plugin->plugin.match = (lms_plugin_match_fn_t)_match;
-    plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
-    plugin->plugin.close = (lms_plugin_close_fn_t)_close;
-    plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
-    plugin->plugin.start = (lms_plugin_start_fn_t)_start;
-    plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
-
-    return (struct lms_plugin *)plugin;
-}
-
-}