Initial checkin
This commit is contained in:
406
metadata.c
Normal file
406
metadata.c
Normal file
@ -0,0 +1,406 @@
|
||||
/* MiniDLNA media server
|
||||
* Copyright (C) 2008 Justin Maggard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <taglib/tag_c.h>
|
||||
#include <libexif/exif-loader.h>
|
||||
|
||||
#include "upnpglobalvars.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#define FLAG_ARTIST 0x01
|
||||
|
||||
char *
|
||||
trim(char *str)
|
||||
{
|
||||
if (!str)
|
||||
return(NULL);
|
||||
int i;
|
||||
for (i=0; i <= strlen(str) && (isspace(str[i]) || str[i] == '"'); i++) {
|
||||
str++;
|
||||
}
|
||||
for (i=(strlen(str)-1); i >= 0 && (isspace(str[i]) || str[i] == '"'); i--) {
|
||||
str[i] = '\0';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char *
|
||||
modifyString(char * string, const char * before, const char * after, short like)
|
||||
{
|
||||
int oldlen, newlen, chgcnt = 0;
|
||||
char *s, *p, *t;
|
||||
|
||||
oldlen = strlen(before);
|
||||
newlen = strlen(after);
|
||||
if( newlen > oldlen )
|
||||
{
|
||||
s = string;
|
||||
while( (p = strstr(s, before)) )
|
||||
{
|
||||
chgcnt++;
|
||||
s = p+oldlen;
|
||||
}
|
||||
string = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1);
|
||||
}
|
||||
|
||||
s = string;
|
||||
while( s )
|
||||
{
|
||||
p = strcasestr(s, before);
|
||||
if( !p )
|
||||
return string;
|
||||
if( like )
|
||||
{
|
||||
t = p+oldlen;
|
||||
while( isspace(*t) )
|
||||
t++;
|
||||
if( *t == '"' )
|
||||
while( *++t != '"' )
|
||||
continue;
|
||||
memmove(t+1, t, strlen(t)+1);
|
||||
*t = '%';
|
||||
}
|
||||
memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
|
||||
memcpy(p, after, newlen);
|
||||
s = p + newlen;
|
||||
}
|
||||
if( newlen < oldlen )
|
||||
string = realloc(string, strlen(string)+1);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
void
|
||||
strip_ext(char * name)
|
||||
{
|
||||
if( rindex(name, '.') )
|
||||
*rindex(name, '.') = '\0';
|
||||
}
|
||||
|
||||
sqlite_int64
|
||||
GetAudioMetadata(const char * path, char * name)
|
||||
{
|
||||
size_t size = 0;
|
||||
char date[16], duration[16], dlna_pn[24], mime[16];
|
||||
struct stat file;
|
||||
int seconds, minutes;
|
||||
sqlite_int64 ret;
|
||||
TagLib_File *audio_file;
|
||||
TagLib_Tag *tag;
|
||||
const TagLib_AudioProperties *properties;
|
||||
char *sql;
|
||||
char *zErrMsg = NULL;
|
||||
char *title, *artist, *album, *genre, *comment;
|
||||
int free_flags = 0;
|
||||
|
||||
if ( stat(path, &file) == 0 )
|
||||
size = file.st_size;
|
||||
else
|
||||
return 0;
|
||||
strip_ext(name);
|
||||
|
||||
taglib_set_strings_unicode(1);
|
||||
|
||||
audio_file = taglib_file_new(path);
|
||||
if(audio_file == NULL)
|
||||
return 0;
|
||||
|
||||
tag = taglib_file_tag(audio_file);
|
||||
properties = taglib_file_audioproperties(audio_file);
|
||||
|
||||
seconds = taglib_audioproperties_length(properties) % 60;
|
||||
minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
|
||||
date[0] = '\0';
|
||||
if( taglib_tag_year(tag) )
|
||||
sprintf(date, "%04d-01-01", taglib_tag_year(tag));
|
||||
sprintf(duration, "%d:%02d:%02d.000", minutes/60, minutes, seconds);
|
||||
|
||||
title = taglib_tag_title(tag);
|
||||
if( strlen(title) )
|
||||
{
|
||||
title = trim(title);
|
||||
if( index(title, '&') )
|
||||
{
|
||||
title = modifyString(strdup(title), "&", "&amp;", 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
title = name;
|
||||
}
|
||||
artist = taglib_tag_artist(tag);
|
||||
if( strlen(artist) )
|
||||
{
|
||||
artist = trim(artist);
|
||||
if( index(artist, '&') )
|
||||
{
|
||||
free_flags |= FLAG_ARTIST;
|
||||
artist = modifyString(strdup(artist), "&", "&amp;", 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
artist = NULL;
|
||||
}
|
||||
album = taglib_tag_album(tag);
|
||||
if( strlen(album) )
|
||||
{
|
||||
album = trim(album);
|
||||
if( index(album, '&') )
|
||||
{
|
||||
album = modifyString(strdup(album), "&", "&amp;", 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
album = NULL;
|
||||
}
|
||||
genre = taglib_tag_genre(tag);
|
||||
if( strlen(genre) )
|
||||
{
|
||||
genre = trim(genre);
|
||||
if( index(genre, '&') )
|
||||
{
|
||||
genre = modifyString(strdup(genre), "&", "&amp;", 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
genre = NULL;
|
||||
}
|
||||
comment = taglib_tag_comment(tag);
|
||||
if( strlen(comment) )
|
||||
{
|
||||
comment = trim(comment);
|
||||
if( index(comment, '&') )
|
||||
{
|
||||
comment = modifyString(strdup(comment), "&", "&amp;", 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
comment = NULL;
|
||||
}
|
||||
|
||||
|
||||
if( 1 ) // Switch on audio file type
|
||||
{
|
||||
strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
||||
strcpy(mime, "audio/mpeg");
|
||||
}
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
|
||||
" TITLE, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
|
||||
"VALUES"
|
||||
" (%d, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
|
||||
size, duration, taglib_audioproperties_channels(properties),
|
||||
taglib_audioproperties_bitrate(properties)*1024,
|
||||
taglib_audioproperties_samplerate(properties),
|
||||
strlen(date) ? date : NULL,
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
genre,
|
||||
comment,
|
||||
taglib_tag_track(tag),
|
||||
dlna_pn, mime);
|
||||
|
||||
taglib_tag_free_strings();
|
||||
taglib_file_free(audio_file);
|
||||
|
||||
if( free_flags & FLAG_ARTIST )
|
||||
free(artist);
|
||||
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||
{
|
||||
fprintf(stderr, "Error inserting details for '%s'! [%s]\n", path, zErrMsg);
|
||||
if (zErrMsg)
|
||||
sqlite3_free(zErrMsg);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sqlite_int64
|
||||
GetImageMetadata(const char * path, char * name)
|
||||
{
|
||||
ExifData *ed;
|
||||
ExifEntry *e = NULL;
|
||||
ExifTag tag;
|
||||
int width=0, height=0, thumb=0;
|
||||
size_t size;
|
||||
char date[64], make[32], model[64], dlna_pn[64];
|
||||
char b[1024];
|
||||
struct stat file;
|
||||
sqlite_int64 ret;
|
||||
char *sql;
|
||||
char *zErrMsg = NULL;
|
||||
|
||||
date[0] = '\0';
|
||||
model[0] = '\0';
|
||||
dlna_pn[0] = '\0';
|
||||
|
||||
//DEBUG printf("Parsing %s...\n", path);
|
||||
if ( stat(path, &file) == 0 )
|
||||
size = file.st_size;
|
||||
else
|
||||
return 0;
|
||||
strip_ext(name);
|
||||
//DEBUG printf(" * size: %d\n", size);
|
||||
|
||||
ExifLoader * l = exif_loader_new();
|
||||
exif_loader_write_file(l, path);
|
||||
ed = exif_loader_get_data(l);
|
||||
exif_loader_unref(l);
|
||||
|
||||
tag = EXIF_TAG_PIXEL_X_DIMENSION;
|
||||
e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], tag);
|
||||
if( e )
|
||||
width = atoi( exif_entry_get_value(e, b, sizeof(b)) );
|
||||
|
||||
tag = EXIF_TAG_PIXEL_Y_DIMENSION;
|
||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
|
||||
if( e )
|
||||
height = atoi( exif_entry_get_value(e, b, sizeof(b)) );
|
||||
//DEBUG printf(" * resolution: %dx%d\n", width, height);
|
||||
|
||||
tag = EXIF_TAG_DATE_TIME_ORIGINAL;
|
||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
|
||||
if( e ) {
|
||||
strncpy(date, exif_entry_get_value(e, b, sizeof(b)), sizeof(date));
|
||||
if( strlen(date) > 10 )
|
||||
{
|
||||
date[4] = '-';
|
||||
date[7] = '-';
|
||||
date[10] = 'T';
|
||||
}
|
||||
else {
|
||||
strcpy(date, "0000-00-00");
|
||||
}
|
||||
}
|
||||
else {
|
||||
strcpy(date, "0000-00-00");
|
||||
}
|
||||
//DEBUG printf(" * date: %s\n", date);
|
||||
|
||||
model[0] = '\0';
|
||||
tag = EXIF_TAG_MAKE;
|
||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tag);
|
||||
if( e )
|
||||
{
|
||||
strncpy(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make));
|
||||
tag = EXIF_TAG_MODEL;
|
||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tag);
|
||||
if( e )
|
||||
{
|
||||
strncpy(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
|
||||
if( !strcasestr(model, make) )
|
||||
snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
|
||||
}
|
||||
}
|
||||
if( !strlen(model) )
|
||||
strcpy(model, "Unknown");
|
||||
//DEBUG printf(" * model: %s\n", model);
|
||||
|
||||
if( ed->size )
|
||||
thumb = 1;
|
||||
else
|
||||
thumb = 0;
|
||||
//DEBUG printf(" * thumbnail: %d\n", thumb);
|
||||
|
||||
exif_data_unref(ed);
|
||||
|
||||
if( width <= 640 && height <= 480 )
|
||||
strcpy(dlna_pn, "JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
else if( width <= 1024 && height <= 768 )
|
||||
strcpy(dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
else if( width <= 4096 && height <= 4096 )
|
||||
strcpy(dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
else
|
||||
strcpy(dlna_pn, "JPEG_XL");
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (TITLE, SIZE, DATE, WIDTH, HEIGHT, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
||||
"VALUES"
|
||||
" ('%q', %d, '%s', %d, %d, %d, '%q', '%s', '%s');",
|
||||
name, size, date, width, height, thumb, model, dlna_pn, "image/jpeg");
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||
{
|
||||
fprintf(stderr, "Error inserting details for '%s'! [%s]\n", path, zErrMsg);
|
||||
if (zErrMsg)
|
||||
sqlite3_free(zErrMsg);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sqlite_int64
|
||||
GetVideoMetadata(const char * path, char * name)
|
||||
{
|
||||
size_t size = 0;
|
||||
struct stat file;
|
||||
char *sql;
|
||||
char *zErrMsg = NULL;
|
||||
int ret;
|
||||
|
||||
//DEBUG printf("Parsing %s...\n", path);
|
||||
if ( stat(path, &file) == 0 )
|
||||
size = file.st_size;
|
||||
strip_ext(name);
|
||||
//DEBUG printf(" * size: %d\n", size);
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (TITLE, SIZE, MIME) "
|
||||
"VALUES"
|
||||
" ('%q', %d, %Q);",
|
||||
name, size, "video/mpeg");
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||
{
|
||||
fprintf(stderr, "Error inserting details for '%s'! [%s]\n", path, zErrMsg);
|
||||
if (zErrMsg)
|
||||
sqlite3_free(zErrMsg);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user