* Make Xbox360 support more generic for use with other PlaysForSure clients.
This commit is contained in:
parent
6c907d9f50
commit
c97d359fe2
@ -42,6 +42,7 @@ enum client_types {
|
||||
EDenonReceiver,
|
||||
EFreeBox,
|
||||
EPopcornHour,
|
||||
EMediaRoom,
|
||||
EStandardDLNA150 = 100
|
||||
};
|
||||
|
||||
|
39
scanner.c
39
scanner.c
@ -539,24 +539,27 @@ int
|
||||
CreateDatabase(void)
|
||||
{
|
||||
int ret, i;
|
||||
const char * containers[] = { "0","-1", "root",
|
||||
"1", "0", _("Music"),
|
||||
"1$4", "1", _("All Music"),
|
||||
"1$5", "1", _("Genre"),
|
||||
"1$6", "1", _("Artist"),
|
||||
"1$7", "1", _("Album"),
|
||||
MUSIC_DIR_ID, "1", _("Folders"),
|
||||
MUSIC_PLIST_ID, "1", _("Playlists"),
|
||||
"2", "0", _("Video"),
|
||||
"2$8", "2", _("All Video"),
|
||||
VIDEO_DIR_ID, "2", _("Folders"),
|
||||
"3", "0", _("Pictures"),
|
||||
"3$11", "3", _("All Pictures"),
|
||||
"3$12", "3", _("Date Taken"),
|
||||
"3$13", "3", _("Camera"),
|
||||
IMAGE_DIR_ID, "3", _("Folders"),
|
||||
"64", "0", _("Browse Folders"),
|
||||
0 };
|
||||
const char * containers[] = { "0","-1", "root",
|
||||
MUSIC_ID, "0", _("Music"),
|
||||
MUSIC_ALL_ID, "1", _("All Music"),
|
||||
MUSIC_GENRE_ID, "1", _("Genre"),
|
||||
MUSIC_ARTIST_ID, "1", _("Artist"),
|
||||
MUSIC_ALBUM_ID, "1", _("Album"),
|
||||
MUSIC_DIR_ID, "1", _("Folders"),
|
||||
MUSIC_PLIST_ID, "1", _("Playlists"),
|
||||
|
||||
VIDEO_ID, "0", _("Video"),
|
||||
VIDEO_ALL_ID, "2", _("All Video"),
|
||||
VIDEO_DIR_ID, "2", _("Folders"),
|
||||
|
||||
IMAGE_ID, "0", _("Pictures"),
|
||||
IMAGE_ALL_ID, "3", _("All Pictures"),
|
||||
IMAGE_DATE_ID, "3", _("Date Taken"),
|
||||
IMAGE_CAMERA_ID, "3", _("Camera"),
|
||||
IMAGE_DIR_ID, "3", _("Folders"),
|
||||
|
||||
BROWSEDIR_ID, "0", _("Browse Folders"),
|
||||
0 };
|
||||
|
||||
ret = sql_exec(db, "CREATE TABLE OBJECTS ( "
|
||||
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
|
37
scanner.h
37
scanner.h
@ -10,11 +10,38 @@
|
||||
#ifndef __SCANNER_H__
|
||||
#define __SCANNER_H__
|
||||
|
||||
#define BROWSEDIR_ID "64"
|
||||
#define MUSIC_DIR_ID "1$14"
|
||||
#define MUSIC_PLIST_ID "1$F"
|
||||
#define VIDEO_DIR_ID "2$15"
|
||||
#define IMAGE_DIR_ID "3$16"
|
||||
/* Try to be generally PlaysForSure compatible by using similar IDs */
|
||||
#define BROWSEDIR_ID "64"
|
||||
|
||||
#define MUSIC_ID "1"
|
||||
#define MUSIC_ALL_ID "1$4"
|
||||
#define MUSIC_GENRE_ID "1$5"
|
||||
#define MUSIC_ARTIST_ID "1$6"
|
||||
#define MUSIC_ALBUM_ID "1$7"
|
||||
#define MUSIC_PLIST_ID "1$F"
|
||||
#define MUSIC_DIR_ID "1$14"
|
||||
#define MUSIC_CONTRIB_ARTIST_ID "1$100"
|
||||
#define MUSIC_ALBUM_ARTIST_ID "1$107"
|
||||
#define MUSIC_COMPOSER_ID "1$108"
|
||||
#define MUSIC_RATING_ID "1$101"
|
||||
|
||||
#define VIDEO_ID "2"
|
||||
#define VIDEO_ALL_ID "2$8"
|
||||
#define VIDEO_GENRE_ID "2$9"
|
||||
#define VIDEO_ACTOR_ID "2$A"
|
||||
#define VIDEO_SERIES_ID "2$E"
|
||||
#define VIDEO_PLIST_ID "2$10"
|
||||
#define VIDEO_DIR_ID "2$15"
|
||||
#define VIDEO_RATING_ID "2$200"
|
||||
|
||||
#define IMAGE_ID "3"
|
||||
#define IMAGE_ALL_ID "3$B"
|
||||
#define IMAGE_DATE_ID "3$C"
|
||||
#define IMAGE_ALBUM_ID "3$D"
|
||||
#define IMAGE_CAMERA_ID "3$D2" // PlaysForSure == Keyword
|
||||
#define IMAGE_PLIST_ID "3$11"
|
||||
#define IMAGE_DIR_ID "3$16"
|
||||
#define IMAGE_RATING_ID "3$300"
|
||||
|
||||
extern int valid_cache;
|
||||
|
||||
|
79
sql.c
79
sql.c
@ -16,7 +16,9 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sql.h"
|
||||
#include "log.h"
|
||||
|
||||
@ -120,4 +122,79 @@ sql_get_int_field(sqlite3 *db, const char *fmt, ...)
|
||||
sqlite3_finalize(stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
sql_get_text_field(void *dbh, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int counter, result, len;
|
||||
char *sql;
|
||||
char *str;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (dbh == NULL)
|
||||
{
|
||||
DPRINTF(E_WARN, L_DB_SQL, "%s: dbh is NULL", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sql = sqlite3_vmprintf(fmt, ap);
|
||||
|
||||
//DPRINTF(E_DEBUG, L_DB_SQL, "sql: %s\n", sql);
|
||||
|
||||
switch (sqlite3_prepare_v2(dbh, sql, -1, &stmt, NULL))
|
||||
{
|
||||
case SQLITE_OK:
|
||||
break;
|
||||
default:
|
||||
DPRINTF(E_ERROR, L_DB_SQL, "prepare failed: %s\n%s\n", sqlite3_errmsg, sql);
|
||||
sqlite3_free(sql);
|
||||
return NULL;
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
|
||||
for (counter = 0;
|
||||
((result = sqlite3_step(stmt)) == SQLITE_BUSY || result == SQLITE_LOCKED) && counter < 2;
|
||||
counter++)
|
||||
{
|
||||
/* While SQLITE_BUSY has a built in timeout,
|
||||
* SQLITE_LOCKED does not, so sleep */
|
||||
if (result == SQLITE_LOCKED)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SQLITE_DONE:
|
||||
/* no rows returned */
|
||||
str = NULL;
|
||||
break;
|
||||
|
||||
case SQLITE_ROW:
|
||||
if (sqlite3_column_type(stmt, 0) == SQLITE_NULL)
|
||||
{
|
||||
str = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
len = sqlite3_column_bytes(stmt, 0);
|
||||
if ((str = sqlite3_malloc(len + 1)) == NULL)
|
||||
{
|
||||
DPRINTF(E_ERROR, L_DB_SQL, "malloc failed");
|
||||
break;
|
||||
}
|
||||
|
||||
strncpy(str, (char *)sqlite3_column_text(stmt, 0), len + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTF(E_WARN, L_DB_SQL, "%s: step failed: %s", __func__, sqlite3_errmsg);
|
||||
str = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return str;
|
||||
}
|
||||
|
3
sql.h
3
sql.h
@ -21,4 +21,7 @@ sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int
|
||||
int
|
||||
sql_get_int_field(sqlite3 *db, const char *fmt, ...);
|
||||
|
||||
char *
|
||||
sql_get_text_field(void *dbh, const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
@ -240,6 +240,7 @@ intervening space) by either an integer or the keyword "infinite". */
|
||||
{
|
||||
h->req_client = EXbox;
|
||||
h->reqflags |= FLAG_MIME_AVI_AVI;
|
||||
h->reqflags |= FLAG_MS_PFS;
|
||||
}
|
||||
else if(strncmp(p, "PLAYSTATION", 11)==0)
|
||||
{
|
||||
@ -269,6 +270,11 @@ intervening space) by either an integer or the keyword "infinite". */
|
||||
h->req_client = EPopcornHour;
|
||||
h->reqflags |= FLAG_MIME_FLAC_FLAC;
|
||||
}
|
||||
else if(strstrc(p, "Microsoft-IPTV-Client", '\r'))
|
||||
{
|
||||
h->req_client = EMediaRoom;
|
||||
h->reqflags |= FLAG_MS_PFS;
|
||||
}
|
||||
else if(strstrc(p, "DLNADOC/1.50", '\r'))
|
||||
{
|
||||
h->req_client = EStandardDLNA150;
|
||||
|
@ -54,8 +54,8 @@ struct upnphttp {
|
||||
off_t req_RangeStart;
|
||||
off_t req_RangeEnd;
|
||||
long int req_chunklen;
|
||||
u_int32_t reqflags;
|
||||
int respflags;
|
||||
uint32_t reqflags;
|
||||
uint32_t respflags;
|
||||
/* response */
|
||||
char * res_buf;
|
||||
int res_buflen;
|
||||
@ -87,6 +87,7 @@ struct upnphttp {
|
||||
#define FLAG_MIME_AVI_AVI 0x00400000
|
||||
#define FLAG_MIME_FLAC_FLAC 0x00800000
|
||||
#define FLAG_NO_RESIZE 0x01000000
|
||||
#define FLAG_MS_PFS 0x02000000 // Microsoft PlaysForSure client
|
||||
|
||||
/* New_upnphttp() */
|
||||
struct upnphttp *
|
||||
|
99
upnpsoap.c
99
upnpsoap.c
@ -689,15 +689,14 @@ callback(void *args, int argc, char **argv, char **azColName)
|
||||
if( album_art && atoi(album_art) )
|
||||
{
|
||||
/* Video and audio album art is handled differently */
|
||||
if( *mime == 'v' && (passed_args->filter & FILTER_RES) && (passed_args->client != EXbox) ) {
|
||||
if( *mime == 'v' && (passed_args->filter & FILTER_RES) && (passed_args->flags & FLAG_MS_PFS) ) {
|
||||
ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN\">"
|
||||
"http://%s:%d/AlbumArt/%s-%s.jpg"
|
||||
"</res>",
|
||||
lan_addr[0].str, runtime_vars.port, album_art, detailID);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
else if( passed_args->filter & FILTER_UPNP_ALBUMARTURI ) {
|
||||
} else if( passed_args->filter & FILTER_UPNP_ALBUMARTURI ) {
|
||||
ret = sprintf(str_buf, "<upnp:albumArtURI ");
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
@ -712,6 +711,27 @@ callback(void *args, int argc, char **argv, char **azColName)
|
||||
passed_args->size += ret;
|
||||
}
|
||||
}
|
||||
#ifdef PFS_HACK
|
||||
if( (passed_args->flags & FLAG_MS_PFS) && *mime == 'i' ) {
|
||||
ret = snprintf(str_buf, 512, "<upnp:album>%s</upnp:album>", "[No Keywords]");
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
|
||||
if( tn && atoi(tn) ) {
|
||||
ret = snprintf(str_buf, 512, "<upnp:albumArtURI>"
|
||||
"http://%s:%d/Thumbnails/%s.jpg"
|
||||
"</upnp:albumArtURI>",
|
||||
lan_addr[0].str, runtime_vars.port, detailID);
|
||||
} else {
|
||||
ret = snprintf(str_buf, 512, "<upnp:albumArtURI>"
|
||||
"http://%s:%d/Resized/%s.jpg?width=160,height=160"
|
||||
"</upnp:albumArtURI>",
|
||||
lan_addr[0].str, runtime_vars.port, detailID);
|
||||
}
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
#endif
|
||||
if( passed_args->filter & FILTER_RES ) {
|
||||
mime_to_ext(mime, ext);
|
||||
if( (passed_args->client == EFreeBox) && tn && atoi(tn) ) {
|
||||
@ -736,7 +756,7 @@ callback(void *args, int argc, char **argv, char **azColName)
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
|
||||
if( passed_args->client == EXbox )
|
||||
if( passed_args->flags & FLAG_MS_PFS )
|
||||
ret = sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024);
|
||||
else
|
||||
ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
|
||||
@ -893,8 +913,8 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
if( !BrowseFlag || (strcmp(BrowseFlag, "BrowseDirectChildren") && strcmp(BrowseFlag, "BrowseMetadata")) )
|
||||
{
|
||||
SoapError(h, 402, "Invalid Args");
|
||||
if( h->req_client == EXbox )
|
||||
ObjectId = malloc(1);
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
ObjectId = sqlite3_malloc(1);
|
||||
goto browse_error;
|
||||
}
|
||||
if( !ObjectId )
|
||||
@ -902,8 +922,8 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
if( !(ObjectId = GetValueFromNameValueList(&data, "ContainerID")) )
|
||||
{
|
||||
SoapError(h, 701, "No such object error");
|
||||
if( h->req_client == EXbox )
|
||||
ObjectId = malloc(1);
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
ObjectId = sqlite3_malloc(1);
|
||||
goto browse_error;
|
||||
}
|
||||
}
|
||||
@ -928,14 +948,20 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
args.requested = RequestedCount;
|
||||
args.client = h->req_client;
|
||||
args.flags = h->reqflags;
|
||||
if( h->req_client == EXbox )
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
{
|
||||
if( strcmp(ObjectId, "16") == 0 )
|
||||
ObjectId = strdup(IMAGE_DIR_ID);
|
||||
else if( strcmp(ObjectId, "15") == 0 )
|
||||
ObjectId = strdup(VIDEO_DIR_ID);
|
||||
if( strchr(ObjectId, '$') || (strcmp(ObjectId, "0") == 0) )
|
||||
{
|
||||
ObjectId = sqlite3_mprintf("%s", ObjectId);
|
||||
}
|
||||
else
|
||||
ObjectId = strdup(ObjectId);
|
||||
{
|
||||
ptr = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
|
||||
"where OBJECT_ID in ('1$%s', '2$%s', '3$%s')",
|
||||
ObjectId, ObjectId, ObjectId);
|
||||
if( ptr )
|
||||
ObjectId = ptr;
|
||||
}
|
||||
}
|
||||
DPRINTF(E_DEBUG, L_HTTP, "Browsing ContentDirectory:\n"
|
||||
" * ObjectID: %s\n"
|
||||
@ -1023,9 +1049,9 @@ browse_error:
|
||||
if( orderBy )
|
||||
free(orderBy);
|
||||
free(resp);
|
||||
if( h->req_client == EXbox )
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
{
|
||||
free(ObjectId);
|
||||
sqlite3_free(ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1068,10 +1094,13 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
StartingIndex = atoi(ptr);
|
||||
if( !ContainerID )
|
||||
{
|
||||
SoapError(h, 701, "No such object error");
|
||||
if( h->req_client == EXbox )
|
||||
ContainerID = malloc(1);
|
||||
goto search_error;
|
||||
if( !(ContainerID = GetValueFromNameValueList(&data, "ObjectID")) )
|
||||
{
|
||||
SoapError(h, 701, "No such object error");
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
ContainerID = sqlite3_malloc(1);
|
||||
goto search_error;
|
||||
}
|
||||
}
|
||||
memset(&args, 0, sizeof(args));
|
||||
|
||||
@ -1094,20 +1123,22 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
args.requested = RequestedCount;
|
||||
args.client = h->req_client;
|
||||
args.flags = h->reqflags;
|
||||
if( h->req_client == EXbox )
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
{
|
||||
if( strcmp(ContainerID, "4") == 0 )
|
||||
ContainerID = strdup("1$4");
|
||||
else if( strcmp(ContainerID, "5") == 0 )
|
||||
ContainerID = strdup("1$5");
|
||||
else if( strcmp(ContainerID, "6") == 0 )
|
||||
ContainerID = strdup("1$6");
|
||||
else if( strcmp(ContainerID, "7") == 0 )
|
||||
ContainerID = strdup("1$7");
|
||||
else if( strcmp(ContainerID, "F") == 0 )
|
||||
ContainerID = strdup(MUSIC_PLIST_ID);
|
||||
if( strchr(ContainerID, '$') || (strcmp(ContainerID, "0") == 0) )
|
||||
{
|
||||
ContainerID = sqlite3_mprintf("%s", ContainerID);
|
||||
}
|
||||
else
|
||||
ContainerID = strdup(ContainerID);
|
||||
{
|
||||
ptr = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
|
||||
"where OBJECT_ID in ('1$%s', '2$%s', '3$%s')",
|
||||
ContainerID, ContainerID, ContainerID);
|
||||
if( ptr )
|
||||
ContainerID = ptr;
|
||||
else
|
||||
ContainerID = sqlite3_mprintf("%s", ContainerID);
|
||||
}
|
||||
#if 0 // Looks like the 360 already does this
|
||||
/* Sort by track number for some containers */
|
||||
if( orderBy &&
|
||||
@ -1261,9 +1292,9 @@ search_error:
|
||||
if( newSearchCriteria )
|
||||
free(newSearchCriteria);
|
||||
free(resp);
|
||||
if( h->req_client == EXbox )
|
||||
if( h->reqflags & FLAG_MS_PFS )
|
||||
{
|
||||
free(ContainerID);
|
||||
sqlite3_free(ContainerID);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user