From ccd9b957f6b6377bf027bcdf8ce0358449e3d144 Mon Sep 17 00:00:00 2001 From: Justin Maggard Date: Thu, 30 Oct 2008 06:53:17 +0000 Subject: [PATCH] Add some video metadata support through libdlna (>0.2.3). Some more changes for DLNA compliance. Reformat some other code. --- Makefile | 2 +- metadata.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++----- metadata.h | 19 +++++++++ scanner.c | 43 ++++++++++++-------- upnphttp.c | 11 ++++-- upnpsoap.c | 84 +++++++++++++++++++-------------------- 6 files changed, 200 insertions(+), 73 deletions(-) diff --git a/Makefile b/Makefile index 7d1c62e..76f13a9 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \ ALLOBJS = $(BASEOBJS) $(LNXOBJS) #LIBS = -liptc -LIBS = -lexif -ltag_c -lsqlite3 #-lgd +LIBS = -lexif -ltag_c -lsqlite3 -ldlna #-lgd TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o diff --git a/metadata.c b/metadata.c index 845a4f1..b73eaaf 100644 --- a/metadata.c +++ b/metadata.c @@ -24,9 +24,11 @@ #include #include #include +#include #include "upnpglobalvars.h" #include "metadata.h" +#include "sql.h" #define FLAG_ARTIST 0x01 @@ -98,6 +100,27 @@ strip_ext(char * name) *rindex(name, '.') = '\0'; } +sqlite_int64 +GetFolderMetadata(const char * name, const char * artist) +{ + char * sql; + int ret; + + sql = sqlite3_mprintf( "INSERT into DETAILS" + " (TITLE, ARTIST) " + "VALUES" + " ('%q', %Q);", + name, artist); + + if( sql_exec(db, sql) != SQLITE_OK ) + ret = 0; + else + ret = sqlite3_last_insert_rowid(db); + sqlite3_free(sql); + + return ret; +} + sqlite_int64 GetAudioMetadata(const char * path, char * name) { @@ -258,16 +281,17 @@ GetImageMetadata(const char * path, char * name) ExifTag tag; int width=0, height=0, thumb=0; size_t size; - char date[64], make[32], model[64], dlna_pn[64]; + char date[64], make[32], model[64]; char b[1024]; struct stat file; sqlite_int64 ret; char *sql; char *zErrMsg = NULL; + metadata_t m; + memset(&m, '\0', sizeof(metadata_t)); date[0] = '\0'; model[0] = '\0'; - dlna_pn[0] = '\0'; //DEBUG printf("Parsing %s...\n", path); if ( stat(path, &file) == 0 ) @@ -277,6 +301,9 @@ GetImageMetadata(const char * path, char * name) strip_ext(name); //DEBUG printf(" * size: %d\n", size); + /* MIME hard-coded to JPEG for now, until we add PNG support */ + asprintf(&m.mime, "image/jpeg"); + ExifLoader * l = exif_loader_new(); exif_loader_write_file(l, path); ed = exif_loader_get_data(l); @@ -340,19 +367,20 @@ GetImageMetadata(const char * path, char * name) exif_data_unref(ed); if( width <= 640 && height <= 480 ) - strcpy(dlna_pn, "JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); + asprintf(&m.dlna_pn, "JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); else if( width <= 1024 && height <= 768 ) - strcpy(dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); + asprintf(&m.dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); else if( width <= 4096 && height <= 4096 ) - strcpy(dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); + asprintf(&m.dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); else - strcpy(dlna_pn, "JPEG_XL"); + asprintf(&m.dlna_pn, "JPEG_XL"); + asprintf(&m.resolution, "%dx%d", width, height); sql = sqlite3_mprintf( "INSERT into DETAILS" - " (TITLE, SIZE, DATE, WIDTH, HEIGHT, THUMBNAIL, CREATOR, DLNA_PN, MIME) " + " (TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) " "VALUES" - " ('%q', %d, '%s', %d, %d, %d, '%q', '%s', '%s');", - name, size, date, width, height, thumb, model, dlna_pn, "image/jpeg"); + " ('%q', %d, '%s', %Q, %d, '%q', %Q, %Q);", + name, size, date, m.resolution, thumb, model, m.dlna_pn, m.mime); //DEBUG printf("SQL: %s\n", sql); if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK ) { @@ -366,6 +394,12 @@ GetImageMetadata(const char * path, char * name) ret = sqlite3_last_insert_rowid(db); } sqlite3_free(sql); + if( m.resolution ) + free(m.resolution); + if( m.dlna_pn ) + free(m.dlna_pn); + if( m.mime ) + free(m.mime); return ret; } @@ -374,9 +408,14 @@ GetVideoMetadata(const char * path, char * name) { size_t size = 0; struct stat file; + dlna_t *dlna; + dlna_profile_t *p; + dlna_item_t *item; char *sql; char *zErrMsg = NULL; int ret; + metadata_t m; + memset(&m, '\0', sizeof(m)); //DEBUG printf("Parsing %s...\n", path); if ( stat(path, &file) == 0 ) @@ -384,11 +423,53 @@ GetVideoMetadata(const char * path, char * name) strip_ext(name); //DEBUG printf(" * size: %d\n", size); + dlna = dlna_init(); + dlna_register_all_media_profiles(dlna); + + item = dlna_item_new (dlna, path); + if (item) + { + if (item->properties) + { + if( strlen(item->properties->duration) ) + m.duration = item->properties->duration; + if( item->properties->bitrate ) + asprintf(&m.bitrate, "%d", item->properties->bitrate); + if( item->properties->sample_frequency ) + asprintf(&m.frequency, "%d", item->properties->sample_frequency); + if( item->properties->bps ) + asprintf(&m.bps, "%d", item->properties->bps); + if( item->properties->channels ) + asprintf(&m.channels, "%d", item->properties->channels); + m.resolution = item->properties->resolution; + } + } + + p = dlna_guess_media_profile (dlna, path); + if (p) + { + m.mime = (char *)p->mime; + asprintf(&m.dlna_pn, "%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", p->id); + } + else + printf ("Unknown format [%s]\n", path); + sql = sqlite3_mprintf( "INSERT into DETAILS" + " (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION," + " TITLE, DLNA_PN, MIME) " + "VALUES" + " (%d, %Q, %d, %d, %d, %Q, '%q', %Q, '%q');", + size, m.duration, + item->properties ? item->properties->channels : 0, + item->properties ? item->properties->bitrate : 0, + item->properties ? item->properties->sample_frequency : 0, + m.resolution, name, + m.dlna_pn, m.mime); +/* sql = sqlite3_mprintf( "INSERT into DETAILS" " (TITLE, SIZE, MIME) " "VALUES" " ('%q', %d, %Q);", - name, size, "video/mpeg"); + name, size, "video/mpeg");*/ //DEBUG printf("SQL: %s\n", sql); if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK ) { @@ -402,5 +483,18 @@ GetVideoMetadata(const char * path, char * name) ret = sqlite3_last_insert_rowid(db); } sqlite3_free(sql); + dlna_item_free(item); + dlna_uninit(dlna); + if( m.dlna_pn ) + free(m.dlna_pn); + if( m.bitrate ) + free(m.bitrate); + if( m.frequency ) + free(m.frequency); + if( m.bps ) + free(m.bps); + if( m.channels ) + free(m.channels); + return ret; } diff --git a/metadata.h b/metadata.h index 4c2eff2..dc08cf6 100644 --- a/metadata.h +++ b/metadata.h @@ -10,9 +10,28 @@ #ifndef __METADATA_H__ #define __METADATA_H__ +typedef struct metadata_s { + char *title; + char *artist; + char *album; + char *genre; + char *comment; + char *channels; + char *bitrate; + char *frequency; + char *bps; + char *resolution; + char *duration; + char *mime; + char *dlna_pn; +} metadata_t; + char * modifyString(char * string, const char * before, const char * after, short like); +sqlite_int64 +GetFolderMetadata(const char * name, const char * artist); + sqlite_int64 GetAudioMetadata(const char * path, char * name); diff --git a/scanner.c b/scanner.c index b2c140e..259742b 100644 --- a/scanner.c +++ b/scanner.c @@ -58,13 +58,14 @@ 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, long unsigned int detailID) +insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent, const char *class, const char *artist) { 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); @@ -95,10 +96,11 @@ insert_container(const char * tmpTable, const char * item, const char * rootPare { parentID = 0; } + detailID = GetFolderMetadata(item, artist); sql = sqlite3_mprintf( "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) " "VALUES" - " ('%s$%X', '%s', %lu, 'container.%s', '%q')", + " ('%s$%X', '%s', %lld, 'container.%s', '%q')", rootParent, parentID, rootParent, detailID, class, item); ret = sql_exec(db, sql); sqlite3_free(sql); @@ -136,7 +138,7 @@ insert_containers(const char * name, const char *path, const char * refID, const strncpy(date_taken, date, 10); if( date ) { - container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", 0); + container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", NULL); parentID = container>>32; objectID = container; sql = sqlite3_mprintf( "INSERT into OBJECTS" @@ -149,12 +151,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", 0); + container = insert_container("CAMS", cam, "3$13", NULL, "storageFolder", 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", 0); + long long int subcontainer = insert_container("CAMDATE", date_taken, parent, cam, "storageFolder", NULL); int subParentID = subcontainer>>32; int subObjectID = subcontainer; sql = sqlite3_mprintf( "INSERT into OBJECTS" @@ -195,7 +197,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", 0); + container = insert_container("ARTISTS", artist, "1$6", NULL, "person.musicArtist", NULL); parentID = container>>32; objectID = container; last_artist_objectID = objectID; @@ -219,7 +221,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", detailID); + container = insert_container("ALBUMS", album, "1$7", NULL, "album.musicAlbum", artist); parentID = container>>32; objectID = container; last_album_objectID = objectID; @@ -243,7 +245,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", 0); + container = insert_container("GENRES", genre, "1$5", NULL, "genre.musicGenre", NULL); parentID = container>>32; objectID = container; last_genre_objectID = objectID; @@ -272,22 +274,30 @@ insert_containers(const char * name, const char *path, const char * refID, const int insert_directory(const char * name, const char * path, const char * parentID, int objectID) { - char *sql; + char * sql; int ret, i; + 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 }; + detailID = GetFolderMetadata(name, NULL); for( i=0; base[i]; i++ ) { sql = sqlite3_mprintf( "INSERT into OBJECTS" - " (OBJECT_ID, PARENT_ID, CLASS, PATH, NAME) " + " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, PATH, NAME) " "VALUES" - " ('%s%s$%X', '%s%s', '%s', '%q', '%q')", - base[i], parentID, objectID, base[i], parentID, class, path, name); + " ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q', '%q')", + base[i], parentID, objectID, base[i], parentID, refID, detailID, class, path, 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); + return -1; } @@ -401,8 +411,7 @@ create_database(void) "CHANNELS INTEGER, " "TRACK INTEGER, " "DATE DATE, " - "WIDTH TEXT, " - "HEIGHT TEXT, " + "RESOLUTION TEXT, " "THUMBNAIL BOOL DEFAULT 0, " "CREATOR TEXT, " "DLNA_PN TEXT, " @@ -412,8 +421,10 @@ create_database(void) goto sql_failed; for( i=0; containers[i]; i=i+3 ) { - sprintf(sql_buf, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, CLASS, NAME) values ( '%s', '%s', 'container.storageFolder', '%s')", - containers[i], containers[i+1], containers[i+2]); + 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]); ret = sql_exec(db, sql_buf); if( ret != SQLITE_OK ) goto sql_failed; diff --git a/upnphttp.c b/upnphttp.c index cf5edec..a27810f 100644 --- a/upnphttp.c +++ b/upnphttp.c @@ -195,7 +195,7 @@ printf("Range Start-End: %lld-%lld\n", h->req_RangeStart, h->req_RangeEnd); p = colon + 1; while(isspace(*p)) p++; - if( strcmp(p, "1") != 0 ) + if( (*p != '1') || !isspace(p[1]) ) h->reqflags |= FLAG_INVALID_REQ; } else if(strncasecmp(line, "TimeSeekRange.dlna.org", 22)==0) @@ -549,6 +549,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_buf); ParseHttpHeaders(h); if( (h->reqflags & FLAG_CHUNKED) && (h->req_chunklen > (h->req_buflen - h->req_contentoff) || h->req_chunklen < 0) ) @@ -567,7 +568,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h) { if( ((strcmp(h->HttpVer, "HTTP/1.1")==0) && !(h->reqflags & FLAG_HOST)) || (h->reqflags & FLAG_INVALID_REQ) ) { - syslog(LOG_NOTICE, "No Host specified in HTTP headers, responding ERROR 400"); + syslog(LOG_NOTICE, "Invalid request, responding ERROR 400. (No Host specified in HTTP headers?)"); Send400(h); } else if( h->reqflags & FLAG_TIMESEEK ) @@ -1181,7 +1182,11 @@ SendResp_dlnafile(struct upnphttp * h, char * object) } else //if( h->reqflags & FLAG_XFERINTERACTIVE ) { - strcat(header, "transferMode.dlna.org: Interactive\r\n"); + if( (strncmp(mime, "vide", 4) == 0) || + (strncmp(mime, "audi", 4) == 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" diff --git a/upnpsoap.c b/upnpsoap.c index a32f90b..cd73310 100644 --- a/upnpsoap.c +++ b/upnpsoap.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "config.h" #include "upnpglobalvars.h" @@ -30,9 +31,8 @@ #include "upnpreplyparse.h" #include "getifaddr.h" -#include #include "metadata.h" -#include +#include "sql.h" static void BuildSendAndCloseSoapResp(struct upnphttp * h, @@ -254,14 +254,12 @@ 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], *name = argv[7], *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], *width = argv[21], *height = argv[22], *tn = argv[23], - *creator = argv[24], *dlna_pn = argv[25], *mime = argv[26]; + char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *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]; char dlna_buf[64]; char str_buf[4096]; - //char * str_buf = malloc(4096); char **result; int ret; @@ -269,20 +267,13 @@ static int callback(void *args, int argc, char **argv, char **azColName) if( passed_args->requested && (passed_args->returned >= passed_args->requested) ) return 0; - //if( (strncmp(class, "item", 4) == 0) && !mime ) // Useless listing if there is no MIME type - // return 0; passed_args->returned++; if( dlna_pn ) - //sprintf(dlna_buf, "DLNA.ORG_PN=%s;DLNA.ORG_OP=01", dlna_pn); sprintf(dlna_buf, "DLNA.ORG_PN=%s", dlna_pn); else strcpy(dlna_buf, "*"); - /*for(i=0; iresp, str_buf); if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) { sprintf(str_buf, "<dc:description>%s</dc:description>", comment); @@ -346,8 +337,8 @@ static int callback(void *args, int argc, char **argv, char **azColName) sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels); strcat(passed_args->resp, str_buf); } - if( width && height && (!passed_args->filter || strstr(passed_args->filter, "res@resolution")) ) { - sprintf(str_buf, "resolution=\"%sx%s\" ", width, height); + if( resolution && (!passed_args->filter || strstr(passed_args->filter, "res@resolution")) ) { + sprintf(str_buf, "resolution=\"%s\" ", resolution); strcat(passed_args->resp, str_buf); } sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">" @@ -382,15 +373,24 @@ static int callback(void *args, int argc, char **argv, char **azColName) ret = sqlite3_get_table(db, str_buf, &result, 0, 0, 0); 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")) { + if( !passed_args->filter || strstr(passed_args->filter, "@childCount")) + { sprintf(str_buf, "childCount=\"%s\"", result[1]); strcat(passed_args->resp, str_buf); } + /* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s */ + if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) ) + { + strcat(passed_args->resp, ">" + "<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>" + "<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>" + "<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass"); + } sprintf(str_buf, ">" "<dc:title>%s</dc:title>" "<upnp:class>object.%s</upnp:class>" "</container>", - name, class); + title, class); sqlite3_free_table(result); } strcat(passed_args->resp, str_buf); @@ -410,15 +410,10 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) static const char resp2[] = "0"; char *resp = calloc(1, 1048576); - strcpy(resp, resp0); - char str_buf[4096]; - char str_buf2[4096]; - memset(str_buf, '\0', sizeof(str_buf)); - memset(str_buf2, '\0', sizeof(str_buf2)); char *zErrMsg = 0; + char *sql; int ret; - char sql_buf[4096]; struct Response { char *resp; int returned; int requested; int total; char *filter; } args; struct NameValueParserData data; @@ -432,8 +427,11 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) if( !ObjectId ) ObjectId = GetValueFromNameValueList(&data, "ContainerID"); + memset(str_buf, '\0', sizeof(str_buf)); memset(&args, 0, sizeof(args)); - args.total = 0; + strcpy(resp, resp0); + + args.total = StartingIndex; args.returned = 0; args.requested = RequestedCount; args.resp = NULL; @@ -458,16 +456,17 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) if( strcmp(BrowseFlag, "BrowseMetadata") == 0 ) { args.requested = 1; - sprintf(sql_buf, "SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s';", ObjectId); - 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 = '%s';", ObjectId); + ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg); } else { - sprintf(sql_buf, "SELECT * 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 limit %d, -1;", - ObjectId, StartingIndex); - 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 PARENT_ID = '%s' order by d.TRACK, d.TITLE, o.NAME limit %d, -1;", + ObjectId, StartingIndex); + ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg); } + sqlite3_free(sql); if( ret != SQLITE_OK ){ printf("SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); @@ -492,13 +491,11 @@ SearchContentDirectory(struct upnphttp * h, const char * action) static const char resp1[] = "</DIDL-Lite>"; static const char resp2[] = "0"; - char *resp = calloc(8, 16384); - strcpy(resp, resp0); - + char *resp = calloc(1, 1048576); + char *zErrMsg = 0; + char sql_buf[4096]; char str_buf[4096]; - char str_buf2[4096]; - memset(str_buf, '\0', sizeof(str_buf)); - memset(str_buf2, '\0', sizeof(str_buf2)); + int ret; struct Response { char *resp; int returned; int requested; int total; char *filter; } args; struct NameValueParserData data; @@ -510,7 +507,9 @@ SearchContentDirectory(struct upnphttp * h, const char * action) char * SearchCriteria = GetValueFromNameValueList(&data, "SearchCriteria"); char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); + memset(str_buf, '\0', sizeof(str_buf)); memset(&args, 0, sizeof(args)); + args.total = 0; args.returned = 0; args.requested = RequestedCount; @@ -523,6 +522,8 @@ SearchContentDirectory(struct upnphttp * h, const char * action) printf("Asked for Filter: %s\n", Filter); if( SortCriteria ) printf("Asked for SortCriteria: %s\n", SortCriteria); + strcpy(resp, resp0); + if( !Filter ) { ClearNameValueList(&data); @@ -553,11 +554,8 @@ SearchContentDirectory(struct upnphttp * h, const char * action) SearchCriteria = modifyString(SearchCriteria, "@refID", "REF_ID", 0); SearchCriteria = modifyString(SearchCriteria, "object.", "", 0); } - printf("Asked for SearchCriteria: %s\n", SearchCriteria); + printf("Translated SearchCriteria: %s\n", SearchCriteria); - char *zErrMsg = 0; - int ret; - char sql_buf[4096]; 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;",