* 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:
parent
4925fa0587
commit
8cab1a2800
4
Makefile
4
Makefile
@ -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
|
||||
|
||||
|
110
metadata.c
110
metadata.c
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
139
scanner.c
139
scanner.c
@ -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 )
|
||||
{
|
||||
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);
|
||||
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[i], parentID, objectID, base[i], parentID, refID, detailID, class, name);
|
||||
base, parentID, objectID, base, parentID, refID, detailID, class, name);
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
ret = sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
if( !i )
|
||||
asprintf(&refID, "%s%s$%X", base[0], parentID, objectID);
|
||||
}
|
||||
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);
|
||||
if( parent )
|
||||
{
|
||||
chdir(dirname((char*)dir));
|
||||
if( !parent )
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Scanning \"%s\" finished!\n", dir);
|
||||
#if USE_FORK
|
||||
|
15
sql.c
15
sql.c
@ -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
2
sql.h
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
259
upnphttp.c
259
upnphttp.c
@ -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)
|
||||
{
|
||||
@ -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,17 +1289,21 @@ 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"
|
||||
"Connection: close\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
|
||||
|
@ -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
|
||||
|
123
upnpsoap.c
123
upnpsoap.c
@ -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, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", 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, "<upnp:albumArtURI %s"
|
||||
">http://%s:5555/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
(!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, "<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\">"
|
||||
"http://%s:5555/MediaItems/%s"
|
||||
"http://%s:5555/MediaItems/%s.dat"
|
||||
"</res>",
|
||||
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, "<res ");
|
||||
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:5555/Thumbnails/%s"
|
||||
"http://%s:5555/Thumbnails/%s.dat"
|
||||
"</res>",
|
||||
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, "<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, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
"<upnp:class>object.%s</upnp:class>"
|
||||
"</container>",
|
||||
"<upnp:class>object.%s</upnp:class>",
|
||||
title, class);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
|
||||
sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
|
||||
sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
|
||||
sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", 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, "<upnp:albumArtURI dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\""
|
||||
">http://%s:5555/AlbumArt/%s.jpg</upnp:albumArtURI>", lan_addr[0].str, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, "</container>");
|
||||
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>"
|
||||
"<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/\">\n";
|
||||
"<DIDL-Lite"
|
||||
CONTENT_DIRECTORY_SCHEMAS;
|
||||
static const char resp1[] = "</DIDL-Lite></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, ">\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>"
|
||||
"<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/\">\n";
|
||||
"<DIDL-Lite"
|
||||
CONTENT_DIRECTORY_SCHEMAS;
|
||||
static const char resp1[] = "</DIDL-Lite></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, ">\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, "&") )
|
||||
{
|
||||
newSearchCriteria = modifyString(strdup(SearchCriteria), "&", "&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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user