* Add some conditional statements to work with more ffmpeg library versions.

* Fix a crash bug when generating a friendly name if the hostname does not contain a dot.
* Implement folder browsing by media type.
* Add initial DLNA-compliant music album art support. (Using a <=160x160 cover.jpg, or folder.jpg, etc)
This commit is contained in:
Justin Maggard 2009-01-30 08:50:09 +00:00
parent 4925fa0587
commit 8cab1a2800
13 changed files with 433 additions and 303 deletions

View File

@ -28,12 +28,12 @@ BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpreplyparse.o minixml.o \
getifaddr.o daemonize.o upnpglobalvars.o \
options.o minissdp.o upnpevents.o \
sql.o metadata.o scanner.o
sql.o utils.o metadata.o albumart.o scanner.o
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
#LIBS = -liptc
LIBS = -lexif -ljpeg -ltag_c -lsqlite3 -lavformat -luuid #-lgd
LIBS = -lexif -ljpeg -ltag_c -lid3tag -lsqlite3 -lavformat -luuid #-lgd
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o

View File

@ -37,6 +37,8 @@
#include "upnpglobalvars.h"
#include "metadata.h"
#include "albumart.h"
#include "utils.h"
#include "sql.h"
#define FLAG_ARTIST 0x01
@ -52,81 +54,6 @@
#define AAC 0x00000100
#define AAC_MULT5 0x00000200
int
ends_with(const char * haystack, const char * needle)
{
const char *found = strcasestr(haystack, needle);
return (found && found[strlen(needle)] == '\0');
}
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';
}
/* This function shamelessly copied from libdlna */
#define MPEG_TS_SYNC_CODE 0x47
#define MPEG_TS_PACKET_LENGTH_DLNA 192 /* prepends 4 bytes to TS packet */
@ -162,16 +89,17 @@ dlna_timestamp_is_present(const char * filename)
}
sqlite_int64
GetFolderMetadata(const char * name, const char * artist)
GetFolderMetadata(const char * name, const char * artist, const char * genre, const char * album_art)
{
char * sql;
int ret;
sql = sqlite3_mprintf( "INSERT into DETAILS"
" (TITLE, ARTIST) "
" (TITLE, CREATOR, ARTIST, GENRE, ALBUM_ART) "
"VALUES"
" ('%q', %Q);",
name, artist);
" ('%q', %Q, %Q, %Q, %lld);",
name, artist, artist, genre,
album_art ? strtoll(album_art, NULL, 10) : 0);
if( sql_exec(db, sql) != SQLITE_OK )
ret = 0;
@ -307,9 +235,9 @@ GetAudioMetadata(const char * path, char * name)
sql = sqlite3_mprintf( "INSERT into DETAILS"
" (PATH, SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
" TITLE, CREATOR, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
" TITLE, CREATOR, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME, ALBUM_ART) "
"VALUES"
" (%Q, %llu, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
" (%Q, %llu, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s', %lld);",
path, size, duration, taglib_audioproperties_channels(properties),
taglib_audioproperties_bitrate(properties)*1024,
taglib_audioproperties_samplerate(properties),
@ -320,8 +248,8 @@ GetAudioMetadata(const char * path, char * name)
genre,
comment,
taglib_tag_track(tag),
dlna_pn, mime);
dlna_pn, mime,
find_album_art(path) );
taglib_tag_free_strings();
taglib_file_free(audio_file);
@ -552,7 +480,7 @@ GetVideoMetadata(const char * path, char * name)
int audio_profile = 0;
ts_timestamp_t ts_timestamp = NONE;
int duration, hours, min, sec, ms;
aac_object_type_t aac_type = 0;
aac_object_type_t aac_type = AAC_INVALID;
metadata_t m;
memset(&m, '\0', sizeof(m));
date[0] = '\0';
@ -597,9 +525,15 @@ GetVideoMetadata(const char * path, char * name)
case CODEC_ID_AAC:
if( !ctx->streams[audio_stream]->codec->extradata_size ||
!ctx->streams[audio_stream]->codec->extradata )
{
printf("No AAC type\n");
}
else
aac_type = ctx->streams[audio_stream]->codec->extradata[0] >> 3;
{
uint8_t data;
memcpy(&data, ctx->streams[audio_stream]->codec->extradata, 1);
aac_type = data >> 3;
}
switch( aac_type )
{
/* AAC Low Complexity variants */
@ -642,15 +576,21 @@ GetVideoMetadata(const char * path, char * name)
else if ( ctx->streams[audio_stream]->codec->bit_rate <= 385000 )
audio_profile = WMA_FULL;
break;
#ifdef CODEC_ID_WMAPRO
case CODEC_ID_WMAPRO:
audio_profile = WMA_PRO;
break;
#endif
case CODEC_ID_MP2:
audio_profile = MP2;
break;
default:
if( (ctx->streams[audio_stream]->codec->codec_id >= CODEC_ID_PCM_S16LE) &&
#ifdef CODEC_ID_PCM_F64LE
(ctx->streams[audio_stream]->codec->codec_id <= CODEC_ID_PCM_F64LE) )
#else
(ctx->streams[audio_stream]->codec->codec_id <= CODEC_ID_PCM_S24DAUD) )
#endif
audio_profile = PCM;
else
printf("Unhandled audio codec [%X]\n", ctx->streams[audio_stream]->codec->codec_id);

View File

@ -44,7 +44,7 @@ char *
modifyString(char * string, const char * before, const char * after, short like);
sqlite_int64
GetFolderMetadata(const char * name, const char * artist);
GetFolderMetadata(const char * name, const char * artist, const char * genre, const char * album_art);
sqlite_int64
GetAudioMetadata(const char * path, char * name);

View File

@ -211,12 +211,15 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
void
getfriendlyname(char * buf, int len)
{
char * dot = NULL;
char * hn = calloc(1, 256);
if( gethostname(hn, 256) == 0 )
{
strncpy(buf, hn, len-1);
buf[len] = '\0';
*strstr(buf, ".") = '\0';
dot = index(buf, '.');
if( dot )
*dot = '\0';
}
else
{

151
scanner.c
View File

@ -28,6 +28,7 @@
#include "upnpglobalvars.h"
#include "metadata.h"
#include "utils.h"
#include "sql.h"
#include "scanner.h"
@ -57,24 +58,24 @@ is_image(const char * file)
}
long long int
insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent, const char *class, const char *artist)
insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent,
const char *class, const char *artist, const char *genre, const char *album_art)
{
char **result;
char *sql;
int cols, rows, ret;
char *zErrMsg = NULL;
int parentID = 0, objectID = 0;
sqlite_int64 detailID;
sql = sqlite3_mprintf("SELECT * from %s where ITEM = '%q' and SUBITEM = '%q'", tmpTable, item, subParent);
ret = sql_get_table(db, sql, &result, &rows, &cols, &zErrMsg);
ret = sql_get_table(db, sql, &result, &rows, &cols);
sqlite3_free(sql);
if( cols )
{
sscanf(result[4], "%X", &parentID);
sqlite3_free_table(result);
sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s$%X'", rootParent, parentID);
ret = sql_get_table(db, sql, &result, 0, &cols, &zErrMsg);
ret = sql_get_table(db, sql, &result, 0, &cols);
sqlite3_free(sql);
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &objectID) == 1) )
{
@ -85,7 +86,7 @@ insert_container(const char * tmpTable, const char * item, const char * rootPare
{
sqlite3_free_table(result);
sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s'", rootParent);
sql_get_table(db, sql, &result, &rows, &cols, &zErrMsg);
sql_get_table(db, sql, &result, &rows, &cols);
sqlite3_free(sql);
if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &parentID) == 1) )
{
@ -95,7 +96,7 @@ insert_container(const char * tmpTable, const char * item, const char * rootPare
{
parentID = 0;
}
detailID = GetFolderMetadata(item, artist);
detailID = GetFolderMetadata(item, artist, genre, album_art);
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) "
"VALUES"
@ -120,13 +121,12 @@ insert_containers(const char * name, const char *path, const char * refID, const
char **result;
int ret;
int cols, row;
char *zErrMsg = NULL;
long long int container;
int parentID;
int objectID = -1;
sprintf(sql_buf, "SELECT * from DETAILS where ID = %lu", detailID);
ret = sql_get_table(db, sql_buf, &result, &row, &cols, &zErrMsg);
ret = sql_get_table(db, sql_buf, &result, &row, &cols);
if( strstr(class, "imageItem") )
{
@ -143,7 +143,7 @@ insert_containers(const char * name, const char *path, const char * refID, const
{
strncpy(date_taken, date, 10);
}
container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", NULL);
container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", NULL, NULL, NULL);
parentID = container>>32;
objectID = container;
sql = sqlite3_mprintf( "INSERT into OBJECTS"
@ -156,12 +156,12 @@ insert_containers(const char * name, const char *path, const char * refID, const
}
if( cam && date )
{
container = insert_container("CAMS", cam, "3$13", NULL, "storageFolder", NULL);
container = insert_container("CAMS", cam, "3$13", NULL, "storageFolder", NULL, NULL, NULL);
parentID = container>>32;
//objectID = container;
char parent[64];
sprintf(parent, "3$13$%X", parentID);
long long int subcontainer = insert_container("CAMDATE", date_taken, parent, cam, "storageFolder", NULL);
long long int subcontainer = insert_container("CAMDATE", date_taken, parent, cam, "storageFolder", NULL, NULL, NULL);
int subParentID = subcontainer>>32;
int subObjectID = subcontainer;
sql = sqlite3_mprintf( "INSERT into OBJECTS"
@ -183,7 +183,8 @@ insert_containers(const char * name, const char *path, const char * refID, const
}
else if( strstr(class, "audioItem") )
{
char *artist = cols ? result[7+cols]:NULL, *album = cols ? result[8+cols]:NULL, *genre = cols ? result[9+cols]:NULL;
char *artist = cols ? result[7+cols]:NULL, *album = cols ? result[8+cols]:NULL;
char *genre = cols ? result[9+cols]:NULL, *album_art = cols ? result[19+cols]:NULL;
static char last_artist[1024] = "0";
static int last_artist_parentID, last_artist_objectID;
static char last_album[1024];
@ -202,7 +203,7 @@ insert_containers(const char * name, const char *path, const char * refID, const
else
{
strcpy(last_artist, artist);
container = insert_container("ARTISTS", artist, "1$6", NULL, "person.musicArtist", NULL);
container = insert_container("ARTISTS", artist, "1$6", NULL, "person.musicArtist", NULL, genre, NULL);
parentID = container>>32;
objectID = container;
last_artist_objectID = objectID;
@ -226,7 +227,7 @@ insert_containers(const char * name, const char *path, const char * refID, const
else
{
strcpy(last_album, album);
container = insert_container("ALBUMS", album, "1$7", NULL, "album.musicAlbum", artist);
container = insert_container("ALBUMS", album, "1$7", NULL, "album.musicAlbum", artist, genre, album_art);
parentID = container>>32;
objectID = container;
last_album_objectID = objectID;
@ -250,7 +251,7 @@ insert_containers(const char * name, const char *path, const char * refID, const
else
{
strcpy(last_genre, genre);
container = insert_container("GENRES", genre, "1$5", NULL, "genre.musicGenre", NULL);
container = insert_container("GENRES", genre, "1$5", NULL, "genre.musicGenre", NULL, NULL, NULL);
parentID = container>>32;
objectID = container;
last_genre_objectID = objectID;
@ -273,33 +274,82 @@ insert_containers(const char * name, const char *path, const char * refID, const
sql_exec(db, sql);
sqlite3_free(sql);
}
else if( strstr(class, "videoItem") )
{
static int last_all_objectID = 0;
/* All Music */
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
"VALUES"
" ('2$8$%X', '2$8', '%s', '%s', %lu, %Q)",
last_all_objectID++, refID, class, detailID, name);
sql_exec(db, sql);
sqlite3_free(sql);
}
sqlite3_free_table(result);
}
int
insert_directory(const char * name, const char * path, const char * parentID, int objectID)
insert_directory(const char * name, const char * path, const char * base, const char * parentID, int objectID)
{
char * sql;
int ret, i;
int ret, found = 0;
sqlite_int64 detailID;
char * refID = NULL;
char class[] = "container.storageFolder";
const char * const base[] = { BROWSEDIR_ID, MUSIC_DIR_ID, VIDEO_DIR_ID, IMAGE_DIR_ID, 0 };
char * id_buf = NULL;
char * parent_buf = NULL;
char **result;
char *dir = strdup(path);
detailID = GetFolderMetadata(name, NULL);
for( i=0; base[i]; i++ )
if( strcmp(base, BROWSEDIR_ID) != 0 )
asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
if( refID )
{
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
"VALUES"
" ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q')",
base[i], parentID, objectID, base[i], parentID, refID, detailID, class, name);
//DEBUG printf("SQL: %s\n", sql);
ret = sql_exec(db, sql);
dir = dirname(dir);
asprintf(&id_buf, "%s%s$%X", base, parentID, objectID);
asprintf(&parent_buf, "%s%s", base, parentID);
while( !found )
{
sql = sqlite3_mprintf("SELECT count(OBJECT_ID) from OBJECTS where OBJECT_ID = '%s'", id_buf);
if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) )
break;
/* Does not exist. Need to create, and may need to create parents also */
sql = sqlite3_mprintf("SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID);
if( (sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK) && atoi(result[1]) )
{
detailID = atoi(result[1]);
}
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
"VALUES"
" ('%s', '%s', %Q, '%lld', '%s', '%q')",
id_buf, parent_buf, refID, detailID, class, rindex(dir, '/')+1);
sql_exec(db, sql);
if( rindex(id_buf, '$') )
*rindex(id_buf, '$') = '\0';
if( rindex(parent_buf, '$') )
*rindex(parent_buf, '$') = '\0';
if( rindex(refID, '$') )
*rindex(refID, '$') = '\0';
dir = dirname(dir);
}
sqlite3_free(sql);
if( !i )
asprintf(&refID, "%s%s$%X", base[0], parentID, objectID);
free(refID);
return 1;
}
detailID = GetFolderMetadata(name, NULL, NULL, NULL);
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
"VALUES"
" ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q')",
base, parentID, objectID, base, parentID, refID, detailID, class, name);
//DEBUG printf("SQL: %s\n", sql);
ret = sql_exec(db, sql);
sqlite3_free(sql);
if( refID )
free(refID);
@ -314,11 +364,16 @@ insert_file(char * name, const char * path, const char * parentID, int object)
char objectID[64];
unsigned long int detailID = 0;
char base[8];
char * typedir_parentID;
int typedir_objectID;
static long unsigned int fileno = 0;
printf("Scanned %lu files...\r", fileno++); fflush(stdout);
sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
typedir_parentID = strdup(parentID);
sscanf(rindex(typedir_parentID, '$')+1, "%X", &typedir_objectID);
*rindex(typedir_parentID, '$') = '\0';
if( is_image(name) )
{
@ -342,6 +397,8 @@ insert_file(char * name, const char * path, const char * parentID, int object)
if( !detailID )
return -1;
insert_directory(name, path, base, typedir_parentID, typedir_objectID);
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
"VALUES"
@ -349,20 +406,18 @@ insert_file(char * name, const char * path, const char * parentID, int object)
objectID, BROWSEDIR_ID, parentID, class, detailID, name);
//DEBUG printf("SQL: %s\n", sql);
sql_exec(db, sql);
sqlite3_free(sql);
#if 0
sql = sqlite3_mprintf( "INSERT into OBJECTS"
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
"VALUES"
" ('%s%s$%X', '%s%s', '%s', '%s', %lu, '%q', '%q')",
base, parentID, object, base, parentID, objectID, class, detailID, path, name);
" ('%s%s$%X', '%s%s', '%s', '%s', %lu, '%q')",
base, parentID, object, base, parentID, objectID, class, detailID, name);
//DEBUG printf("SQL: %s\n", sql);
sql_exec(db, sql);
sqlite3_free(sql);
#else
insert_containers(name, path, objectID, class, detailID);
#endif
return -1;
return 0;
}
int
@ -426,7 +481,15 @@ create_database(void)
"THUMBNAIL BOOL DEFAULT 0, "
"CREATOR TEXT, "
"DLNA_PN TEXT, "
"MIME TEXT"
"MIME TEXT, "
"ALBUM_ART INTEGER DEFAULT 0"
");");
if( ret != SQLITE_OK )
goto sql_failed;
ret = sql_exec(db, "CREATE TABLE ALBUM_ART ( "
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
"PATH TEXT NOT NULL, "
"EMBEDDED BOOL DEFAULT 0"
");");
if( ret != SQLITE_OK )
goto sql_failed;
@ -435,7 +498,7 @@ create_database(void)
sprintf(sql_buf, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
" values "
"('%s', '%s', %lld, 'container.storageFolder', '%s')",
containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL), containers[i+2]);
containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL, NULL, NULL), containers[i+2]);
ret = sql_exec(db, sql_buf);
if( ret != SQLITE_OK )
goto sql_failed;
@ -453,6 +516,7 @@ create_database(void)
sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
sql_exec(db, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
sql_exec(db, "create INDEX IDX_ALBUM_ART ON ALBUM_ART(ID);");
sql_failed:
@ -523,7 +587,7 @@ ScanDirectory(const char * dir, const char * parent)
}
if( S_ISDIR(entry.st_mode) )
{
insert_directory(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i);
insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i);
sprintf(parent_id, "%s$%X", (parent ? parent:""), i);
ScanDirectory(full_path, parent_id);
}
@ -537,8 +601,11 @@ ScanDirectory(const char * dir, const char * parent)
free(namelist[i]);
}
free(namelist);
chdir(dirname((char*)dir));
if( !parent )
if( parent )
{
chdir(dirname((char*)dir));
}
else
{
printf("Scanning \"%s\" finished!\n", dir);
#if USE_FORK

15
sql.c
View File

@ -25,7 +25,6 @@ sql_exec(sqlite3 * db, const char * sql)
char *errMsg = NULL;
//DEBUG printf("SQL: %s\n", sql);
//ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
if( ret != SQLITE_OK )
{
@ -37,16 +36,18 @@ sql_exec(sqlite3 * db, const char * sql)
}
int
sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg)
sql_get_table(sqlite3 *db, const char *sql, char ***pazResult, int *pnRow, int *pnColumn)
{
//DEBUG printf("SQL: %s\n", zSql);
int ret;
ret = sqlite3_get_table(db, zSql, pazResult, pnRow, pnColumn, pzErrmsg);
char *errMsg = NULL;
//DEBUG printf("SQL: %s\n", sql);
ret = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &errMsg);
if( ret != SQLITE_OK )
{
fprintf(stderr, "SQL ERROR [%s]\n%s\n", *pzErrmsg, zSql);
if (*pzErrmsg)
sqlite3_free(*pzErrmsg);
fprintf(stderr, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
if (errMsg)
sqlite3_free(errMsg);
}
return ret;
}

2
sql.h
View File

@ -16,6 +16,6 @@ int
sql_exec(sqlite3 * db, const char * sql);
int
sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg);
sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn);
#endif

View File

@ -84,31 +84,7 @@ static const char * const upnpallowedvalues[] =
"IN_PROGRESS",
"STOPPED",
0,
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," /* 44 */
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
"http-get:*:audio/x-ms-wma:*,"
"http-get:*:audio/wav:*,"
"http-get:*:audio/mp4:*,"
"http-get:*:audio/x-aiff:*,"
"http-get:*:audio/x-flac:*,"
"http-get:*:application/ogg:*,"
"http-get:*:image/jpeg:*,"
"http-get:*:image/gif:*,"
"http-get:*:audio/x-mpegurl:*,"
"http-get:*:video/mpeg:*,"
"http-get:*:video/x-msvideo:*,"
"http-get:*:video/avi:*,"
"http-get:*:video/mpeg2:*,"
"http-get:*:video/dvd:*,"
"http-get:*:video/x-ms-wmv:*",
RESOURCE_PROTOCOL_INFO_VALUES, /* 44 */
0,
"", /* 46 */
0

View File

@ -16,6 +16,34 @@
#define USE_FORK 1
#define DB_VERSION 1
#define RESOURCE_PROTOCOL_INFO_VALUES \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01," \
"http-get:*:audio/x-ms-wma:*," \
"http-get:*:audio/wav:*," \
"http-get:*:audio/mp4:*," \
"http-get:*:audio/x-aiff:*," \
"http-get:*:audio/x-flac:*," \
"http-get:*:application/ogg:*," \
"http-get:*:image/jpeg:*," \
"http-get:*:image/gif:*," \
"http-get:*:audio/x-mpegurl:*," \
"http-get:*:video/mpeg:*," \
"http-get:*:video/x-msvideo:*," \
"http-get:*:video/avi:*," \
"http-get:*:video/mpeg2:*," \
"http-get:*:video/dvd:*," \
"http-get:*:video/x-ms-wmv:*"
/* file to store all leases */
#ifdef ENABLE_LEASEFILE
extern const char * lease_file;

View File

@ -33,6 +33,7 @@
#include <sys/sendfile.h>
#include "upnpglobalvars.h"
#include "utils.h"
#include <sqlite3.h>
#include <libexif/exif-loader.h>
#if 0 //JPEG_RESIZE
@ -175,7 +176,8 @@ intervening space) by either an integer or the keyword "infinite". */
h->reqflags |= FLAG_RANGE;
h->req_RangeEnd = atoll(index(p+6, '-')+1);
h->req_RangeStart = atoll(p+6);
printf("Range Start-End: %lld - %lld\n", h->req_RangeStart, h->req_RangeEnd?h->req_RangeEnd:-1);
printf("Range Start-End: %lld - %lld\n",
h->req_RangeStart, h->req_RangeEnd?h->req_RangeEnd:-1);
}
}
else if(strncasecmp(line, "Host", 4)==0)
@ -532,7 +534,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
HttpVer[i] = '\0';
syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
HttpCommand, HttpUrl, HttpVer);
//DEBUG printf("HTTP REQUEST:\n%.*s\n", h->req_buflen, h->req_buf);
printf("HTTP REQUEST:\n%.*s\n", h->req_buflen, h->req_buf);
ParseHttpHeaders(h);
/* see if we need to wait for remaining data */
@ -607,6 +609,11 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
SendResp_thumbnail(h, HttpUrl+12);
CloseSocket_upnphttp(h);
}
else if(strncmp(HttpUrl, "/AlbumArt/", 10) == 0)
{
SendResp_albumArt(h, HttpUrl+10);
CloseSocket_upnphttp(h);
}
#if 0 //JPEG_RESIZE
else if(strncmp(HttpUrl, "/Resized/", 7) == 0)
{
@ -833,7 +840,7 @@ void
SendResp_upnphttp(struct upnphttp * h)
{
int n;
printf("HTTP RESPONSE:\n%.*s\n", h->res_buflen, h->res_buf);
printf("HTTP RESPONSE:\n%.*s\n", h->res_buflen, h->res_buf);
n = send(h->socket, h->res_buf, h->res_buflen, 0);
if(n<0)
{
@ -847,6 +854,127 @@ printf("HTTP RESPONSE:\n%.*s\n", h->res_buflen, h->res_buf);
}
}
int
send_data(struct upnphttp * h, char * header, size_t size)
{
int n;
n = send(h->socket, header, size, 0);
if(n<0)
{
syslog(LOG_ERR, "send(res_buf): %m");
}
else if(n < h->res_buflen)
{
/* TODO : handle correctly this case */
syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
n, h->res_buflen);
}
else
{
return 0;
}
return 1;
}
void
send_file(struct upnphttp * h, int sendfd, off_t offset, off_t end_offset)
{
off_t send_size;
while( offset < end_offset )
{
send_size = ( ((end_offset - offset) < MAX_BUFFER_SIZE) ? (end_offset - offset + 1) : MAX_BUFFER_SIZE);
off_t ret = sendfile(h->socket, sendfd, &offset, send_size);
if( ret == -1 )
{
printf("sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
if( errno == 32 || errno == 9 || errno == 54 || errno == 104 )
break;
}
/*else
{
printf("sent %lld bytes to %d. offset is now %lld.\n", ret, h->socket, offset);
}*/
}
}
void
SendResp_albumArt(struct upnphttp * h, char * object)
{
char header[1500];
char sql_buf[256];
char **result;
int rows;
char *path;
char date[30];
time_t curtime = time(NULL);
off_t offset = 0, size;
int sendfh;
memset(header, 0, 1500);
if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE )
{
syslog(LOG_NOTICE, "Hey, you can't specify transferMode as Streaming with an image!");
Send406(h);
return;
}
strip_ext(object);
sprintf(sql_buf, "SELECT PATH from ALBUM_ART where ID = %s", object);
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
if( !rows )
{
syslog(LOG_NOTICE, "ALBUM_ART ID %s not found, responding ERROR 404", object);
Send404(h);
goto error;
}
path = result[1];
printf("Serving album art ID: %s [%s]\n", object, path);
if( access(path, F_OK) == 0 )
{
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
sendfh = open(path, O_RDONLY);
if( sendfh < 0 ) {
printf("Error opening %s\n", path);
goto error;
}
size = lseek(sendfh, 0, SEEK_END);
lseek(sendfh, 0, SEEK_SET);
sprintf(header, "HTTP/1.1 200 OK\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Length: %lld\r\n"
"Connection: close\r\n"
"Date: %s\r\n"
"EXT:\r\n"
"contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n"
"Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA/1.0\r\n",
size, date);
if( h->reqflags & FLAG_XFERBACKGROUND )
{
strcat(header, "transferMode.dlna.org: Background\r\n\r\n");
}
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
{
strcat(header, "transferMode.dlna.org: Interactive\r\n\r\n");
}
if( (send_data(h, header, strlen(header)) == 0) && (h->req_command != EHead) && (sendfh > 0) )
{
send_file(h, sendfh, offset, size);
}
close(sendfh);
}
error:
sqlite3_free_table(result);
}
void
SendResp_thumbnail(struct upnphttp * h, char * object)
{
@ -854,9 +982,9 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
char sql_buf[256];
char **result;
int rows;
char *path;
char date[30];
time_t curtime = time(NULL);
int n;
ExifData *ed;
ExifLoader *l;
@ -869,7 +997,8 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
return;
}
sprintf(sql_buf, "SELECT d.PATH from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
strip_ext(object);
sprintf(sql_buf, "SELECT PATH from DETAILS where ID = '%s'", object);
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
if( !rows )
{
@ -877,20 +1006,23 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
Send404(h);
goto error;
}
printf("Serving up thumbnail for ObjectId: %s [%s]\n", object, result[1]);
path = result[1];
printf("Serving thumbnail for ObjectId: %s [%s]\n", object, path);
if( access(result[1], F_OK) == 0 )
if( access(path, F_OK) == 0 )
{
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
l = exif_loader_new();
exif_loader_write_file(l, result[1]);
exif_loader_write_file(l, path);
ed = exif_loader_get_data(l);
exif_loader_unref(l);
if( !ed->size )
if( !ed || !ed->size )
{
Send404(h);
if( ed )
exif_data_unref(ed);
goto error;
}
sprintf(header, "HTTP/1.1 200 OK\r\n"
@ -900,47 +1032,21 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
"Date: %s\r\n"
"EXT:\r\n"
"contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n"
"Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA_TN/1.0\r\n",
"Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA/1.0\r\n",
ed->size, date);
if( h->reqflags & FLAG_XFERBACKGROUND )
{
strcat(header, "transferMode.dlna.org: Background\r\n");
strcat(header, "transferMode.dlna.org: Background\r\n\r\n");
}
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
{
strcat(header, "transferMode.dlna.org: Interactive\r\n");
}
strcat(header, "\r\n");
n = send(h->socket, header, strlen(header), 0);
if(n<0)
{
syslog(LOG_ERR, "send(res_buf): %m");
}
else if(n < h->res_buflen)
{
/* TODO : handle correctly this case */
syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
n, h->res_buflen);
strcat(header, "transferMode.dlna.org: Interactive\r\n\r\n");
}
if( h->req_command == EHead )
if( (send_data(h, header, strlen(header)) == 0) && (h->req_command != EHead) )
{
exif_data_unref(ed);
goto error;
}
n = send(h->socket, ed->data, ed->size, 0);
if(n<0)
{
syslog(LOG_ERR, "send(res_buf): %m");
}
else if(n < h->res_buflen)
{
/* TODO : handle correctly this case */
syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
n, h->res_buflen);
send_data(h, (char *)ed->data, ed->size);
}
exif_data_unref(ed);
}
@ -1067,15 +1173,17 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
int rows;
char date[30];
time_t curtime = time(NULL);
off_t total, send_size;
off_t total, offset, size;
char *path, *mime, *dlna;
int sendfh;
#if USE_FORK
pid_t newpid = 0;
#endif
memset(header, 0, 1500);
sprintf(sql_buf, "SELECT d.PATH, d.MIME, d.DLNA_PN from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
strip_ext(object);
sprintf(sql_buf, "SELECT PATH, MIME, DLNA_PN from DETAILS where ID = '%s'", object);
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
if( !rows )
{
@ -1093,11 +1201,11 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
path = result[3];
mime = result[4];
dlna = result[5];
printf("ObjectId: %s [%s]\n", object, path);
printf("Serving DetailID: %s [%s]\n", object, path);
if( h->reqflags & FLAG_XFERSTREAMING )
{
if( strncmp(mime, "imag", 4) == 0 )
if( strncmp(mime, "image", 5) == 0 )
{
syslog(LOG_NOTICE, "Hey, you can't specify transferMode as Streaming with an image!");
Send406(h);
@ -1112,7 +1220,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
Send400(h);
goto error;
}
if( strncmp(mime, "imag", 4) != 0 )
if( strncmp(mime, "image", 5) != 0 )
{
syslog(LOG_NOTICE, "Hey, you can't specify transferMode as Interactive without an image!");
Send406(h);
@ -1121,15 +1229,17 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
}
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
off_t offset = h->req_RangeStart;
int sendfh = open(path, O_RDONLY);
offset = h->req_RangeStart;
sendfh = open(path, O_RDONLY);
if( sendfh < 0 ) {
printf("Error opening %s\n", result[2]);
printf("Error opening %s\n", path);
goto error;
}
off_t size = lseek(sendfh, 0, SEEK_END);
size = lseek(sendfh, 0, SEEK_END);
lseek(sendfh, 0, SEEK_SET);
sprintf(header, "HTTP/1.1 20%c OK\r\n"
"Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'), mime);
if( h->reqflags & FLAG_RANGE )
{
if( !h->req_RangeEnd )
@ -1149,10 +1259,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
goto error;
}
sprintf(hdr_buf, "HTTP/1.1 206 OK\r\n"
"Content-Type: %s\r\n", mime);
strcpy(header, hdr_buf);
if( h->req_RangeEnd && (h->req_RangeEnd < size) )
if( h->req_RangeEnd < size )
{
total = h->req_RangeEnd - h->req_RangeStart + 1;
sprintf(hdr_buf, "Content-Length: %llu\r\n"
@ -1172,11 +1279,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
{
h->req_RangeEnd = size;
total = size;
sprintf(hdr_buf, "%s 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %llu\r\n",
"HTTP/1.1", mime, total);
//h->HttpVer, mime, total);
sprintf(hdr_buf, "Content-Length: %llu\r\n", total);
}
strcat(header, hdr_buf);
@ -1186,16 +1289,20 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
}
else if( h->reqflags & FLAG_XFERBACKGROUND )
{
if( strncmp(mime, "imag", 4) == 0 )
if( strncmp(mime, "image", 5) == 0 )
strcat(header, "transferMode.dlna.org: Background\r\n");
}
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
{
if( (strncmp(mime, "video", 5) == 0) ||
(strncmp(mime, "audio", 5) == 0) )
{
strcat(header, "transferMode.dlna.org: Streaming\r\n");
}
else
{
strcat(header, "transferMode.dlna.org: Interactive\r\n");
}
}
sprintf(hdr_buf, "Accept-Ranges: bytes\r\n"
@ -1207,42 +1314,12 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
date, dlna);
strcat(header, hdr_buf);
int n;
n = send(h->socket, header, strlen(header), 0);
if(n<0)
if( (send_data(h, header, strlen(header)) == 0) && (h->req_command != EHead) && (sendfh > 0) )
{
syslog(LOG_ERR, "send(res_buf): %m");
}
else if(n < h->res_buflen)
{
/* TODO : handle correctly this case */
syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
n, h->res_buflen);
send_file(h, sendfh, offset, h->req_RangeEnd);
}
close(sendfh);
if( h->req_command == EHead )
{
close(sendfh);
}
else if( sendfh > 0 )
{
while( offset < h->req_RangeEnd )
{
send_size = (( (h->req_RangeEnd - offset) < MAX_BUFFER_SIZE ) ? (h->req_RangeEnd - offset + 1) : MAX_BUFFER_SIZE);
off_t ret = sendfile(h->socket, sendfh, &offset, send_size);
if( ret == -1 )
{
printf("sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
if( errno == 32 || errno == 9 || errno == 54 || errno == 104 )
break;
}
/*else
{
printf("sent %lld bytes to %d. offset is now %lld.\n", ret, h->socket, offset);
}*/
}
close(sendfh);
}
error:
sqlite3_free_table(result);
#if USE_FORK

View File

@ -121,6 +121,8 @@ BuildResp2_upnphttp(struct upnphttp * h, int respcode,
void
SendResp_upnphttp(struct upnphttp *);
void
SendResp_albumArt(struct upnphttp *, char * url);
void
SendResp_resizedimg(struct upnphttp *, char * url);
void

View File

@ -107,31 +107,7 @@ GetProtocolInfo(struct upnphttp * h, const char * action)
"<u:%sResponse "
"xmlns:u=\"%s\">"
"<Source>"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
"http-get:*:audio/x-ms-wma:*,"
"http-get:*:audio/wav:*,"
"http-get:*:audio/mp4:*,"
"http-get:*:audio/x-aiff:*,"
"http-get:*:audio/x-flac:*,"
"http-get:*:application/ogg:*,"
"http-get:*:image/jpeg:*,"
"http-get:*:image/gif:*,"
"http-get:*:audio/x-mpegurl:*,"
"http-get:*:video/mpeg:*,"
"http-get:*:video/x-msvideo:*,"
"http-get:*:video/avi:*,"
"http-get:*:video/mpeg2:*,"
"http-get:*:video/dvd:*,"
"http-get:*:video/x-ms-wmv:*"
RESOURCE_PROTOCOL_INFO_VALUES
"</Source>"
"<Sink></Sink>"
"</u:%sResponse>";
@ -231,10 +207,10 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
static int callback(void *args, int argc, char **argv, char **azColName)
{
struct Response { char *resp; int returned; int requested; int total; char *filter; } *passed_args = (struct Response *)args;
char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *size = argv[9], *title = argv[10],
char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *detailID = argv[5], *size = argv[9], *title = argv[10],
*duration = argv[11], *bitrate = argv[12], *sampleFrequency = argv[13], *artist = argv[14], *album = argv[15],
*genre = argv[16], *comment = argv[17], *nrAudioChannels = argv[18], *track = argv[19], *date = argv[20],
*resolution = argv[21], *tn = argv[22], *creator = argv[23], *dlna_pn = argv[24], *mime = argv[25];
*resolution = argv[21], *tn = argv[22], *creator = argv[23], *dlna_pn = argv[24], *mime = argv[25], *album_art = argv[26];
char dlna_buf[64];
char str_buf[4096];
char **result;
@ -292,6 +268,14 @@ static int callback(void *args, int argc, char **argv, char **azColName)
sprintf(str_buf, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track);
strcat(passed_args->resp, str_buf);
}
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
sprintf(str_buf, "&lt;upnp:albumArtURI %s"
"&gt;http://%s:5555/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;",
(!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID")) ?
"dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"" : "",
lan_addr[0].str, album_art);
strcat(passed_args->resp, str_buf);
}
if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
strcat(passed_args->resp, "&lt;res ");
if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) {
@ -319,9 +303,9 @@ static int callback(void *args, int argc, char **argv, char **azColName)
strcat(passed_args->resp, str_buf);
}
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:5555/MediaItems/%s"
"http://%s:5555/MediaItems/%s.dat"
"&lt;/res&gt;",
mime, dlna_buf, lan_addr[0].str, id);
mime, dlna_buf, lan_addr[0].str, detailID);
#if 0 //JPEG_RESIZE
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
strcat(passed_args->resp, str_buf);
@ -336,9 +320,9 @@ static int callback(void *args, int argc, char **argv, char **azColName)
strcat(passed_args->resp, str_buf);
strcat(passed_args->resp, "&lt;res ");
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:5555/Thumbnails/%s"
"http://%s:5555/Thumbnails/%s.dat"
"&lt;/res&gt;",
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, id);
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, detailID);
}
strcat(passed_args->resp, str_buf);
}
@ -346,8 +330,8 @@ static int callback(void *args, int argc, char **argv, char **azColName)
}
else if( strncmp(class, "container", 9) == 0 )
{
sprintf(str_buf, "SELECT count(*) from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where PARENT_ID = '%s' order by d.TRACK, d.TITLE, o.NAME;", id);
ret = sqlite3_get_table(db, str_buf, &result, 0, 0, 0);
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
sprintf(str_buf, "&lt;container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
strcat(passed_args->resp, str_buf);
if( !passed_args->filter || strstr(passed_args->filter, "@childCount"))
@ -368,9 +352,27 @@ static int callback(void *args, int argc, char **argv, char **azColName)
}
sprintf(str_buf, "&gt;"
"&lt;dc:title&gt;%s&lt;/dc:title&gt;"
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;"
"&lt;/container&gt;",
"&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
title, class);
strcat(passed_args->resp, str_buf);
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
strcat(passed_args->resp, str_buf);
}
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
strcat(passed_args->resp, str_buf);
}
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
strcat(passed_args->resp, str_buf);
}
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
sprintf(str_buf, "&lt;upnp:albumArtURI dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\""
"&gt;http://%s:5555/AlbumArt/%s.jpg&lt;/upnp:albumArtURI&gt;", lan_addr[0].str, album_art);
strcat(passed_args->resp, str_buf);
}
sprintf(str_buf, "&lt;/container&gt;");
sqlite3_free_table(result);
}
strcat(passed_args->resp, str_buf);
@ -385,7 +387,8 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
"<u:BrowseResponse "
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
"<Result>"
"&lt;DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"&gt;\n";
"&lt;DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS;
static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
static const char resp2[] = "<UpdateID>0</UpdateID></u:BrowseResponse>";
@ -410,6 +413,10 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
memset(str_buf, '\0', sizeof(str_buf));
memset(&args, 0, sizeof(args));
strcpy(resp, resp0);
/* See if we need to include DLNA namespace reference */
if( (strlen(Filter) <= 1) || strstr(Filter, "dlna") )
strcat(resp, DLNA_NAMESPACE);
strcat(resp, "&gt;\n");
args.total = StartingIndex;
args.returned = 0;
@ -467,13 +474,14 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
"<u:SearchResponse "
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
"<Result>"
"&lt;DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"&gt;\n";
"&lt;DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS;
static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
static const char resp2[] = "<UpdateID>0</UpdateID></u:SearchResponse>";
char *resp = calloc(1, 1048576);
char *zErrMsg = 0;
char sql_buf[4096];
char *sql;
char str_buf[4096];
int ret;
struct Response { char *resp; int returned; int requested; int total; char *filter; } args;
@ -486,8 +494,8 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
char * Filter = GetValueFromNameValueList(&data, "Filter");
char * SearchCriteria = GetValueFromNameValueList(&data, "SearchCriteria");
char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria");
char * newSearchCriteria = NULL;
memset(str_buf, '\0', sizeof(str_buf));
memset(&args, 0, sizeof(args));
args.total = 0;
@ -503,6 +511,10 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
if( SortCriteria ) printf("Asked for SortCriteria: %s\n", SortCriteria);
strcpy(resp, resp0);
/* See if we need to include DLNA namespace reference */
if( (strlen(Filter) <= 1) || strstr(Filter, "dlna") )
strcat(resp, DLNA_NAMESPACE);
strcat(resp, "&gt;\n");
if( !Filter )
{
@ -513,10 +525,11 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
if( strlen(Filter) > 1 )
args.filter = Filter;
if( strcmp(ContainerID, "0") == 0 )
*ContainerID = '%';
*ContainerID = '*';
if( !SearchCriteria )
{
asprintf(&SearchCriteria, "1 = 1");
asprintf(&newSearchCriteria, "1 = 1");
SearchCriteria = newSearchCriteria;
}
else
{
@ -533,25 +546,41 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
SearchCriteria = modifyString(SearchCriteria, "exists false", "is NULL", 0);
SearchCriteria = modifyString(SearchCriteria, "@refID", "REF_ID", 0);
SearchCriteria = modifyString(SearchCriteria, "object.", "", 0);
#if 0
if( strstr(SearchCriteria, "&amp;") )
{
newSearchCriteria = modifyString(strdup(SearchCriteria), "&amp;", "&amp;amp;", 0);
SearchCriteria = newSearchCriteria;
}
#endif
}
printf("Translated SearchCriteria: %s\n", SearchCriteria);
args.resp = resp;
sprintf(sql_buf, "SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where OBJECT_ID like '%s$%%' and (%s) order by d.TRACK, d.TITLE, o.NAME limit %d, -1;",
ContainerID, SearchCriteria, StartingIndex);
printf("Search SQL: %s\n", sql_buf);
ret = sqlite3_exec(db, sql_buf, callback, (void *) &args, &zErrMsg);
sql = sqlite3_mprintf("SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where OBJECT_ID glob '%s$*' and (%s) "
"%z"
" order by d.TRACK, d.TITLE, o.NAME limit %d, -1;",
ContainerID, SearchCriteria,
(*ContainerID == '*') ? NULL :
sqlite3_mprintf("UNION ALL SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
" where OBJECT_ID = '%s' and (%s) ", ContainerID, SearchCriteria),
StartingIndex);
printf("Search SQL: %s\n", sql);
ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
if( ret != SQLITE_OK ){
printf("SQL error: %s\n", zErrMsg);
printf("SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql);
sqlite3_free(zErrMsg);
}
sqlite3_free(sql);
strcat(resp, resp1);
sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n<TotalMatches>%u</TotalMatches>\n", args.returned, args.total);
strcat(resp, str_buf);
strcat(resp, resp2);
BuildSendAndCloseSoapResp(h, resp, strlen(resp));
ClearNameValueList(&data);
if( newSearchCriteria )
free(newSearchCriteria);
free(resp);
}

View File

@ -7,6 +7,13 @@
#ifndef __UPNPSOAP_H__
#define __UPNPSOAP_H__
#define CONTENT_DIRECTORY_SCHEMAS \
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" \
" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"" \
" xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\""
#define DLNA_NAMESPACE \
" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""
/* ExecuteSoapAction():
* this method executes the requested Soap Action */
void