diff --git a/Makefile.am b/Makefile.am
index 9b55a8a..9273aa5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,7 +28,7 @@ minidlnad_SOURCES = minidlna.c upnphttp.c upnpdescgen.c upnpsoap.c \
sql.c utils.c metadata.c scanner.c inotify.c \
tivo_utils.c tivo_beacon.c tivo_commands.c \
playlist.c image_utils.c albumart.c log.c \
- tagutils/tagutils.c
+ containers.c tagutils/tagutils.c
#if NEED_VORBIS
vorbisflag = -lvorbis
diff --git a/clients.h b/clients.h
index dc766c6..d30f4ed 100644
--- a/clients.h
+++ b/clients.h
@@ -38,9 +38,7 @@
#define FLAG_FORCE_SORT 0x00000800
#define FLAG_CAPTION_RES 0x00001000
/* Response-related flags */
-#define FLAG_HAS_CAPTIONS 0x20000000
-#define FLAG_FREE_OBJECT_ID 0x40000000
-#define FLAG_ROOT_CONTAINER 0x80000000
+#define FLAG_HAS_CAPTIONS 0x80000000
enum match_types {
EMatchNone,
diff --git a/containers.c b/containers.c
new file mode 100644
index 0000000..8e95ee3
--- /dev/null
+++ b/containers.c
@@ -0,0 +1,170 @@
+/* MiniDLNA media server
+ * Copyright (C) 2014 NETGEAR
+ *
+ * This file is part of MiniDLNA.
+ *
+ * MiniDLNA is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * MiniDLNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MiniDLNA. If not, see .
+ */
+#include
+#include "clients.h"
+#include "minidlnatypes.h"
+#include "scanner.h"
+#include "upnpglobalvars.h"
+#include "containers.h"
+#include "log.h"
+
+#define NINETY_DAYS "7776000"
+
+const char *music_id = MUSIC_ID;
+const char *music_all_id = MUSIC_ALL_ID;
+const char *music_genre_id = MUSIC_GENRE_ID;
+const char *music_artist_id = MUSIC_ARTIST_ID;
+const char *music_album_id = MUSIC_ALBUM_ID;
+const char *music_plist_id = MUSIC_PLIST_ID;
+const char *music_dir_id = MUSIC_DIR_ID;
+const char *video_all_id = VIDEO_ALL_ID;
+const char *video_dir_id = VIDEO_DIR_ID;
+const char *image_all_id = IMAGE_ALL_ID;
+const char *image_date_id = IMAGE_DATE_ID;
+const char *image_camera_id = IMAGE_CAMERA_ID;
+const char *image_dir_id = IMAGE_DIR_ID;
+
+struct magic_container_s magic_containers[] =
+{
+ /* Alternate root container */
+ { NULL,
+ "0",
+ &runtime_vars.root_container,
+ NULL,
+ "0",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ -1,
+ 0,
+ },
+
+ /* Recent 50 audio items */
+ { "Recently Added",
+ "1$FF0",
+ NULL,
+ "\"1$FF0$\" || OBJECT_ID",
+ "\"1$FF0\"",
+ "o.OBJECT_ID",
+ "(select null from DETAILS where MIME glob 'a*' and timestamp > (strftime('%s','now') - "NINETY_DAYS") limit 50)",
+ "MIME glob 'a*' and REF_ID is NULL and timestamp > (strftime('%s','now') - "NINETY_DAYS")",
+ "order by TIMESTAMP DESC",
+ 50,
+ 0,
+ },
+
+ /* Recent 50 video items */
+ { "Recently Added",
+ "2$FF0",
+ NULL,
+ "\"2$FF0$\" || OBJECT_ID",
+ "\"2$FF0\"",
+ "o.OBJECT_ID",
+ "(select null from DETAILS where MIME glob 'v*' and timestamp > (strftime('%s','now') - "NINETY_DAYS") limit 50)",
+ "MIME glob 'v*' and REF_ID is NULL and timestamp > (strftime('%s','now') - "NINETY_DAYS")",
+ "order by TIMESTAMP DESC",
+ 50,
+ 0,
+ },
+
+ /* Recent 50 image items */
+ { "Recently Added",
+ "3$FF0",
+ NULL,
+ "\"3$FF0$\" || OBJECT_ID",
+ "\"3$FF0\"",
+ "o.OBJECT_ID",
+ "(select null from DETAILS where MIME glob 'i*' and timestamp > (strftime('%s','now') - "NINETY_DAYS") limit 50)",
+ "MIME glob 'i*' and REF_ID is NULL and timestamp > (strftime('%s','now') - "NINETY_DAYS")",
+ "order by TIMESTAMP DESC",
+ 50,
+ 0,
+ },
+
+ /* Microsoft PlaysForSure containers */
+ { NULL, "4", &music_all_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "5", &music_genre_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "6", &music_artist_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "7", &music_album_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "8", &video_all_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "B", &image_all_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "C", &image_date_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "F", &music_plist_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "14", &music_dir_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "15", &video_dir_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "16", &image_dir_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+ { NULL, "D2", &image_camera_id, NULL, NULL, NULL, NULL, NULL, NULL, -1, FLAG_MS_PFS },
+
+ /* Jump straight to Music on audio-only devices */
+ { NULL, "0", &music_id, NULL, "0", NULL, NULL, NULL, NULL, -1, FLAG_AUDIO_ONLY },
+
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0 }
+};
+
+struct magic_container_s *
+in_magic_container(const char *id, int flags, const char **real_id)
+{
+ size_t len;
+ int i;
+
+ for (i = 0; magic_containers[i].objectid_match; i++)
+ {
+ if (magic_containers[i].required_flags && !(flags & magic_containers[i].required_flags))
+ continue;
+ if (magic_containers[i].objectid && !(*magic_containers[i].objectid))
+ continue;
+ DPRINTF(E_DEBUG, L_HTTP, "Checking magic container %d [%s]\n", i, magic_containers[i].objectid_match);
+ len = strlen(magic_containers[i].objectid_match);
+ if (strncmp(id, magic_containers[i].objectid_match, len) == 0)
+ {
+ if (*(id+len) == '$')
+ *real_id = id+len+1;
+ else if (*(id+len) == '\0')
+ *real_id = id;
+ else
+ continue;
+ DPRINTF(E_DEBUG, L_HTTP, "Found magic container %d [%s]\n", i, magic_containers[i].objectid_match);
+ return &magic_containers[i];
+ }
+ }
+
+ return NULL;
+}
+
+struct magic_container_s *
+check_magic_container(const char *id, int flags)
+{
+ int i;
+
+ for (i = 0; magic_containers[i].objectid_match; i++)
+ {
+ if (magic_containers[i].required_flags && !(flags & magic_containers[i].required_flags))
+ continue;
+ if (magic_containers[i].objectid && !(*magic_containers[i].objectid))
+ continue;
+ DPRINTF(E_DEBUG, L_HTTP, "Checking magic container %d [%s]\n", i, magic_containers[i].objectid_match);
+ if (strcmp(id, magic_containers[i].objectid_match) == 0)
+ {
+ DPRINTF(E_DEBUG, L_HTTP, "Found magic container %d [%s]\n", i, magic_containers[i].objectid_match);
+ return &magic_containers[i];
+ }
+ }
+
+ return NULL;
+}
diff --git a/containers.h b/containers.h
new file mode 100644
index 0000000..aef77c5
--- /dev/null
+++ b/containers.h
@@ -0,0 +1,36 @@
+/* MiniDLNA media server
+ * Copyright (C) 2014 NETGEAR
+ *
+ * This file is part of MiniDLNA.
+ *
+ * MiniDLNA is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * MiniDLNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MiniDLNA. If not, see .
+ */
+
+struct magic_container_s {
+ const char *name;
+ const char *objectid_match;
+ const char **objectid;
+ const char *objectid_sql;
+ const char *parentid_sql;
+ const char *refid_sql;
+ const char *child_count;
+ const char *where;
+ const char *orderby;
+ int max_count;
+ int required_flags;
+};
+
+extern struct magic_container_s magic_containers[];
+
+struct magic_container_s *in_magic_container(const char *id, int flags, const char **real_id);
+struct magic_container_s *check_magic_container(const char *id, int flags);
diff --git a/minidlnatypes.h b/minidlnatypes.h
index bc4e195..6879b70 100644
--- a/minidlnatypes.h
+++ b/minidlnatypes.h
@@ -49,8 +49,8 @@ struct runtime_vars_s {
int port; /* HTTP Port */
int notify_interval; /* seconds between SSDP announces */
int max_connections; /* max number of simultaneous conenctions */
- char *root_container; /* root ObjectID (instead of "0") */
- char *ifaces[MAX_LAN_ADDR]; /* list of configured network interfaces */
+ const char *root_container; /* root ObjectID (instead of "0") */
+ const char *ifaces[MAX_LAN_ADDR]; /* list of configured network interfaces */
};
struct string_s {
diff --git a/scanner.c b/scanner.c
index 5ae5612..ee3af48 100644
--- a/scanner.c
+++ b/scanner.c
@@ -44,6 +44,7 @@
#include "sql.h"
#include "scanner.h"
#include "albumart.h"
+#include "containers.h"
#include "log.h"
#if SCANDIR_CONST
@@ -580,6 +581,25 @@ CreateDatabase(void)
if( ret != SQLITE_OK )
goto sql_failed;
}
+ for( i=0; magic_containers[i].objectid_match; i++ )
+ {
+ struct magic_container_s *magic = &magic_containers[i];
+ if (!magic->name)
+ continue;
+ if( sql_get_int_field(db, "SELECT 1 from OBJECTS where OBJECT_ID = '%s'", magic->objectid_match) == 0 )
+ {
+ char *parent = strdup(magic->objectid_match);
+ if (strrchr(parent, '$'))
+ *strrchr(parent, '$') = '\0';
+ ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
+ " values "
+ "('%s', '%s', %lld, 'container.storageFolder', '%q')",
+ magic->objectid_match, parent, GetFolderMetadata(magic->name, NULL, NULL, NULL, 0), magic->name);
+ free(parent);
+ if( ret != SQLITE_OK )
+ goto sql_failed;
+ }
+ }
sql_exec(db, "create INDEX IDX_OBJECTS_OBJECT_ID ON OBJECTS(OBJECT_ID);");
sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
diff --git a/upnpsoap.c b/upnpsoap.c
index 8c789bb..d226272 100644
--- a/upnpsoap.c
+++ b/upnpsoap.c
@@ -65,12 +65,19 @@
#include "utils.h"
#include "upnphttp.h"
#include "upnpsoap.h"
+#include "containers.h"
#include "upnpreplyparse.h"
#include "getifaddr.h"
#include "scanner.h"
#include "sql.h"
#include "log.h"
+#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
+# define __SORT_LIMIT if( totalMatches < 10000 )
+#else
+# define __SORT_LIMIT
+#endif
+
static void
BuildSendAndCloseSoapResp(struct upnphttp * h,
const char * body, int bodylen)
@@ -682,10 +689,17 @@ add_res(char *size, char *duration, char *bitrate, char *sampleFrequency,
}
static int
-get_child_count(const char *object)
+get_child_count(const char *object, struct magic_container_s *magic)
{
int ret;
- ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", object);
+
+ if (magic && magic->child_count)
+ ret = sql_get_int_field(db, "SELECT count(*) from %s", magic->child_count);
+ else if (magic && magic->objectid && *(magic->objectid))
+ ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", *(magic->objectid));
+ else
+ ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", object);
+
return (ret > 0) ? ret : 0;
}
@@ -698,11 +712,11 @@ object_exists(const char *object)
return (ret > 0);
}
-#define COLUMNS "o.REF_ID, o.DETAIL_ID, o.CLASS," \
+#define COLUMNS "o.DETAIL_ID, o.CLASS," \
" d.SIZE, d.TITLE, d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST," \
" d.ALBUM, d.GENRE, d.COMMENT, d.CHANNELS, d.TRACK, d.DATE, d.RESOLUTION," \
" d.THUMBNAIL, d.CREATOR, d.DLNA_PN, d.MIME, d.ALBUM_ART, d.DISC "
-#define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.PARENT_ID, " COLUMNS
+#define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.PARENT_ID, o.REF_ID, " COLUMNS
static int
callback(void *args, int argc, char **argv, char **azColName)
@@ -747,9 +761,6 @@ callback(void *args, int argc, char **argv, char **azColName)
}
passed_args->returned++;
- if( runtime_vars.root_container && strcmp(parent, runtime_vars.root_container) == 0 )
- parent = "0";
-
if( strncmp(class, "item", 4) == 0 )
{
uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B;
@@ -1051,10 +1062,10 @@ callback(void *args, int argc, char **argv, char **azColName)
{
ret = strcatf(str, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
if( passed_args->filter & FILTER_SEARCHABLE ) {
- ret = strcatf(str, "searchable=\"1\" ");
+ ret = strcatf(str, "searchable=\"%d\" ", check_magic_container(id, passed_args->flags) ? 0 : 1);
}
if( passed_args->filter & FILTER_CHILDCOUNT ) {
- ret = strcatf(str, "childCount=\"%d\"", get_child_count(id));
+ ret = strcatf(str, "childCount=\"%d\"", get_child_count(id, check_magic_container(id, passed_args->flags)));
}
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
if( passed_args->requested == 1 && strcmp(id, "0") == 0 && (passed_args->filter & FILTER_UPNP_SEARCHCLASS) ) {
@@ -1108,29 +1119,6 @@ callback(void *args, int argc, char **argv, char **azColName)
return 0;
}
-static int
-translate_object_id(char **id, struct Response *args)
-{
- if (args->flags & FLAG_MS_PFS)
- {
- if( !strchr(*id, '$') && (strcmp(*id, "0") != 0) )
- {
- char *s;
- s = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
- " where OBJECT_ID in "
- "('"MUSIC_ID"$%q', '"VIDEO_ID"$%q', '"IMAGE_ID"$%q')",
- *id, *id, *id);
- if (s)
- {
- *id = s;
- args->flags |= FLAG_FREE_OBJECT_ID;
- }
- }
- }
-
- return 0;
-}
-
static void
BrowseContentDirectory(struct upnphttp * h, const char * action)
{
@@ -1140,13 +1128,19 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
""
"<DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS;
+ struct magic_container_s *magic;
char *zErrMsg = NULL;
char *sql, *ptr;
struct Response args;
struct string_s str;
int totalMatches = 0;
int ret;
- char *ObjectID, *Filter, *BrowseFlag, *SortCriteria;
+ const char *ObjectID, *BrowseFlag;
+ char *Filter, *SortCriteria;
+ const char *objectid_sql = "o.OBJECT_ID";
+ const char *parentid_sql = "o.PARENT_ID";
+ const char *refid_sql = "o.REF_ID";
+ char where[256] = "";
char *orderBy = NULL;
struct NameValueParserData data;
int RequestedCount = 0;
@@ -1206,7 +1200,6 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
args.client = h->req_client ? h->req_client->type->type : 0;
args.flags = h->req_client ? h->req_client->type->flags : 0;
args.str = &str;
- translate_object_id(&ObjectID, &args);
DPRINTF(E_DEBUG, L_HTTP, "Browsing ContentDirectory:\n"
" * ObjectID: %s\n"
" * Count: %d\n"
@@ -1217,44 +1210,59 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
ObjectID, RequestedCount, StartingIndex,
BrowseFlag, Filter, SortCriteria);
- if( strcmp(ObjectID, "0") == 0 )
- {
- args.flags |= FLAG_ROOT_CONTAINER;
- if( runtime_vars.root_container )
- {
- if( (args.flags & FLAG_AUDIO_ONLY) && (strcmp(runtime_vars.root_container, BROWSEDIR_ID) == 0) )
- ObjectID = MUSIC_DIR_ID;
- else
- ObjectID = runtime_vars.root_container;
- }
- else
- {
- if( args.flags & FLAG_AUDIO_ONLY )
- ObjectID = MUSIC_ID;
- }
- }
-
if( strcmp(BrowseFlag+6, "Metadata") == 0 )
{
+ const char *id = ObjectID;
args.requested = 1;
- sql = sqlite3_mprintf("SELECT %s, " COLUMNS
- "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
- " where OBJECT_ID = '%q';",
- (args.flags & FLAG_ROOT_CONTAINER) ? "0, -1" : "o.OBJECT_ID, o.PARENT_ID",
- ObjectID);
+ magic = in_magic_container(ObjectID, args.flags, &id);
+ if (magic)
+ {
+ if (magic->objectid_sql && strcmp(id, ObjectID) != 0)
+ objectid_sql = magic->objectid_sql;
+ if (magic->parentid_sql && strcmp(id, ObjectID) != 0)
+ parentid_sql = magic->parentid_sql;
+ if (magic->refid_sql)
+ refid_sql = magic->refid_sql;
+ }
+ sql = sqlite3_mprintf("SELECT %s, %s, %s, " COLUMNS
+ "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
+ " where OBJECT_ID = '%q';",
+ objectid_sql, parentid_sql, refid_sql, id);
ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
totalMatches = args.returned;
}
else
{
+ magic = check_magic_container(ObjectID, args.flags);
+ if (magic)
+ {
+ if (magic->objectid && *(magic->objectid))
+ ObjectID = *(magic->objectid);
+ if (magic->objectid_sql)
+ objectid_sql = magic->objectid_sql;
+ if (magic->parentid_sql)
+ parentid_sql = magic->parentid_sql;
+ if (magic->refid_sql)
+ refid_sql = magic->refid_sql;
+ if (magic->where)
+ strncpyt(where, magic->where, sizeof(where));
+ if (magic->max_count > 0)
+ {
+ ret = get_child_count(ObjectID, magic);
+ totalMatches = ret > magic->max_count ? magic->max_count : ret;
+ if (RequestedCount > magic->max_count || RequestedCount < 0)
+ RequestedCount = magic->max_count;
+ }
+ }
+ if (!where[0])
+ sqlite3_snprintf(sizeof(where), where, "PARENT_ID = '%q'", ObjectID);
+
if (!totalMatches)
- totalMatches = get_child_count(ObjectID);
+ totalMatches = get_child_count(ObjectID, magic);
ret = 0;
if( SortCriteria )
{
-#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
- if( totalMatches < 10000 )
-#endif
+ __SORT_LIMIT
orderBy = parse_sort_criteria(SortCriteria, &ret);
}
else if (!orderBy)
@@ -1268,9 +1276,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
}
else if( args.flags & FLAG_FORCE_SORT )
{
-#ifdef __sparc__
- if( totalMatches < 10000 )
-#endif
+ __SORT_LIMIT
ret = xasprintf(&orderBy, "order by o.CLASS, d.DISC, d.TRACK, d.TITLE");
}
else
@@ -1289,10 +1295,11 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
goto browse_error;
}
- sql = sqlite3_mprintf( SELECT_COLUMNS
+ sql = sqlite3_mprintf("SELECT %s, %s, %s, " COLUMNS
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
- " where PARENT_ID = '%q' %s limit %d, %d;",
- ObjectID, THISORNUL(orderBy), StartingIndex, RequestedCount);
+ " where %s %s limit %d, %d;",
+ objectid_sql, parentid_sql, refid_sql,
+ where, THISORNUL(orderBy), StartingIndex, RequestedCount);
DPRINTF(E_DEBUG, L_HTTP, "Browse SQL: %s\n", sql);
ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
}
@@ -1322,8 +1329,6 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
BuildSendAndCloseSoapResp(h, str.data, str.off);
browse_error:
ClearNameValueList(&data);
- if( args.flags & FLAG_FREE_OBJECT_ID )
- sqlite3_free(ObjectID);
free(orderBy);
free(str.data);
}
@@ -1608,13 +1613,15 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
""
"<DIDL-Lite"
CONTENT_DIRECTORY_SCHEMAS;
+ struct magic_container_s *magic;
char *zErrMsg = NULL;
char *sql, *ptr;
struct Response args;
struct string_s str;
int totalMatches;
int ret;
- char *ContainerID, *Filter, *SearchCriteria, *SortCriteria;
+ const char *ContainerID;
+ char *Filter, *SearchCriteria, *SortCriteria;
char *orderBy = NULL, *where = NULL, sep[] = "$*";
char groupBy[] = "group by DETAIL_ID";
struct NameValueParserData data;
@@ -1663,7 +1670,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
args.client = h->req_client ? h->req_client->type->type : 0;
args.flags = h->req_client ? h->req_client->type->flags : 0;
args.str = &str;
- translate_object_id(&ContainerID, &args);
DPRINTF(E_DEBUG, L_HTTP, "Searching ContentDirectory:\n"
" * ObjectID: %s\n"
" * Count: %d\n"
@@ -1674,8 +1680,12 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
ContainerID, RequestedCount, StartingIndex,
SearchCriteria, Filter, SortCriteria);
+ magic = check_magic_container(ContainerID, args.flags);
+ if (magic && magic->objectid && *(magic->objectid))
+ ContainerID = *(magic->objectid);
+
if( strcmp(ContainerID, "0") == 0 )
- ContainerID[0] = '*';
+ ContainerID = "*";
if( strcmp(ContainerID, MUSIC_ALL_ID) == 0 ||
GETFLAG(DLNA_STRICT_MASK) )
@@ -1706,11 +1716,9 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
goto search_error;
}
}
-#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
ret = 0;
- if( totalMatches < 10000 )
-#endif
- orderBy = parse_sort_criteria(SortCriteria, &ret);
+ __SORT_LIMIT
+ orderBy = parse_sort_criteria(SortCriteria, &ret);
/* If it's a DLNA client, return an error for bad sort criteria */
if( ret < 0 && ((args.flags & FLAG_DLNA) || GETFLAG(DLNA_STRICT_MASK)) )
{
@@ -1746,8 +1754,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
BuildSendAndCloseSoapResp(h, str.data, str.off);
search_error:
ClearNameValueList(&data);
- if( args.flags & FLAG_FREE_OBJECT_ID )
- sqlite3_free(ContainerID);
free(orderBy);
free(where);
free(str.data);