* Fix some issues with ampersand escaping.
* Improve image date/camera metadata handling by not storing a date if none exists. * Add preliminary TiVo video serving support.
This commit is contained in:
parent
0303f15fa4
commit
223df2111b
@ -425,7 +425,9 @@ inotify_insert_directory(int fd, char *name, const char * path)
|
|||||||
if( strcmp(e->d_name, ".") == 0 ||
|
if( strcmp(e->d_name, ".") == 0 ||
|
||||||
strcmp(e->d_name, "..") == 0 )
|
strcmp(e->d_name, "..") == 0 )
|
||||||
continue;
|
continue;
|
||||||
esc_name = modifyString(strdup(e->d_name), "&", "&", 0);
|
esc_name = escape_tag(e->d_name);
|
||||||
|
if( !esc_name )
|
||||||
|
esc_name = strdup(e->d_name);
|
||||||
asprintf(&path_buf, "%s/%s", path, e->d_name);
|
asprintf(&path_buf, "%s/%s", path, e->d_name);
|
||||||
if( e->d_type == DT_DIR )
|
if( e->d_type == DT_DIR )
|
||||||
{
|
{
|
||||||
|
74
metadata.c
74
metadata.c
@ -92,6 +92,23 @@ dlna_timestamp_is_present(const char * filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TIVO_SUPPORT
|
||||||
|
int
|
||||||
|
is_tivo_file(const char * path)
|
||||||
|
{
|
||||||
|
unsigned char buf[5];
|
||||||
|
unsigned char hdr[5] = { 'T','i','V','o','\0' };
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/* read file header */
|
||||||
|
fd = open(path, O_RDONLY);
|
||||||
|
read(fd, buf, 5);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return( !memcmp(buf, hdr, 5) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* This function taken from libavutil (ffmpeg), because it's not included with all versions of libavutil. */
|
/* This function taken from libavutil (ffmpeg), because it's not included with all versions of libavutil. */
|
||||||
int
|
int
|
||||||
get_fourcc(const char *s)
|
get_fourcc(const char *s)
|
||||||
@ -99,44 +116,23 @@ get_fourcc(const char *s)
|
|||||||
return (s[0]) + (s[1]<<8) + (s[2]<<16) + (s[3]<<24);
|
return (s[0]) + (s[1]<<8) + (s[2]<<16) + (s[3]<<24);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
escape_tag(const char *tag)
|
|
||||||
{
|
|
||||||
char *esc_tag = NULL;
|
|
||||||
|
|
||||||
if( index(tag, '&') || index(tag, '<') || index(tag, '>') )
|
|
||||||
{
|
|
||||||
esc_tag = strdup(tag);
|
|
||||||
esc_tag = modifyString(esc_tag, "&", "&amp;", 0);
|
|
||||||
esc_tag = modifyString(esc_tag, "<", "&lt;", 0);
|
|
||||||
esc_tag = modifyString(esc_tag, ">", "&gt;", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return esc_tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite_int64
|
sqlite_int64
|
||||||
GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, const char * album_art, const char * art_dlna_pn)
|
GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, const char * album_art, const char * art_dlna_pn)
|
||||||
{
|
{
|
||||||
char * sql;
|
char * sql;
|
||||||
char * esc_name = NULL;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
esc_name = escape_tag(name);
|
|
||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||||
" (TITLE, PATH, CREATOR, ARTIST, GENRE, ALBUM_ART, ART_DLNA_PN) "
|
" (TITLE, PATH, CREATOR, ARTIST, GENRE, ALBUM_ART, ART_DLNA_PN) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" ('%q', %Q, %Q, %Q, %Q, %lld, %Q);",
|
" ('%q', %Q, %Q, %Q, %Q, %lld, %Q);",
|
||||||
esc_name ? esc_name : name,
|
name, path, artist, artist, genre,
|
||||||
path, artist, artist, genre,
|
|
||||||
album_art ? strtoll(album_art, NULL, 10) : 0,
|
album_art ? strtoll(album_art, NULL, 10) : 0,
|
||||||
art_dlna_pn);
|
art_dlna_pn);
|
||||||
if( sql_exec(db, sql) != SQLITE_OK )
|
if( sql_exec(db, sql) != SQLITE_OK )
|
||||||
ret = 0;
|
ret = 0;
|
||||||
else
|
else
|
||||||
ret = sqlite3_last_insert_rowid(db);
|
ret = sqlite3_last_insert_rowid(db);
|
||||||
if( esc_name )
|
|
||||||
free(esc_name);
|
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -328,7 +324,8 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
FILE *infile;
|
FILE *infile;
|
||||||
int width=0, height=0, thumb=0;
|
int width=0, height=0, thumb=0;
|
||||||
off_t size;
|
off_t size;
|
||||||
char date[64], make[32], model[64];
|
char *date = NULL, *cam = NULL;
|
||||||
|
char make[32], model[64] = {'\0'};
|
||||||
char b[1024];
|
char b[1024];
|
||||||
char *esc_name = NULL;
|
char *esc_name = NULL;
|
||||||
struct stat file;
|
struct stat file;
|
||||||
@ -337,9 +334,6 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
metadata_t m;
|
metadata_t m;
|
||||||
memset(&m, '\0', sizeof(metadata_t));
|
memset(&m, '\0', sizeof(metadata_t));
|
||||||
|
|
||||||
date[0] = '\0';
|
|
||||||
model[0] = '\0';
|
|
||||||
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing %s...\n", path);
|
//DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing %s...\n", path);
|
||||||
if ( stat(path, &file) == 0 )
|
if ( stat(path, &file) == 0 )
|
||||||
size = file.st_size;
|
size = file.st_size;
|
||||||
@ -371,7 +365,7 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
tag = EXIF_TAG_DATE_TIME_ORIGINAL;
|
tag = EXIF_TAG_DATE_TIME_ORIGINAL;
|
||||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
|
e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
|
||||||
if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_DATE_TIME)) ) {
|
if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_DATE_TIME)) ) {
|
||||||
strncpy(date, exif_entry_get_value(e, b, sizeof(b)), sizeof(date));
|
date = strdup(exif_entry_get_value(e, b, sizeof(b)));
|
||||||
if( strlen(date) > 10 )
|
if( strlen(date) > 10 )
|
||||||
{
|
{
|
||||||
date[4] = '-';
|
date[4] = '-';
|
||||||
@ -379,15 +373,12 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
date[10] = 'T';
|
date[10] = 'T';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
strcpy(date, "0000-00-00");
|
free(date);
|
||||||
|
date = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
strcpy(date, "0000-00-00");
|
|
||||||
}
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * date: %s\n", date);
|
//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * date: %s\n", date);
|
||||||
|
|
||||||
model[0] = '\0';
|
|
||||||
tag = EXIF_TAG_MAKE;
|
tag = EXIF_TAG_MAKE;
|
||||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tag);
|
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tag);
|
||||||
if( e )
|
if( e )
|
||||||
@ -400,10 +391,9 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
strncpy(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
|
strncpy(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
|
||||||
if( !strcasestr(model, make) )
|
if( !strcasestr(model, make) )
|
||||||
snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
|
snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
|
||||||
|
cam = strdup(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( !strlen(model) )
|
|
||||||
strcpy(model, "Unknown");
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * model: %s\n", model);
|
//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * model: %s\n", model);
|
||||||
|
|
||||||
if( ed->size )
|
if( ed->size )
|
||||||
@ -452,8 +442,8 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||||
" (PATH, TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
" (PATH, TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
||||||
"VALUES"
|
"VALUES"
|
||||||
" (%Q, '%q', %llu, '%s', %Q, %d, '%q', %Q, %Q);",
|
" (%Q, '%q', %llu, %Q, %Q, %d, %Q, %Q, %Q);",
|
||||||
path, esc_name?esc_name:name, size, date, m.resolution, thumb, model, m.dlna_pn, m.mime);
|
path, esc_name?esc_name:name, size, date, m.resolution, thumb, cam, m.dlna_pn, m.mime);
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_METADATA, "SQL: %s\n", sql);
|
//DEBUG DPRINTF(E_DEBUG, L_METADATA, "SQL: %s\n", sql);
|
||||||
if( sql_exec(db, sql) != SQLITE_OK )
|
if( sql_exec(db, sql) != SQLITE_OK )
|
||||||
{
|
{
|
||||||
@ -473,6 +463,10 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
free(m.mime);
|
free(m.mime);
|
||||||
if( esc_name )
|
if( esc_name )
|
||||||
free(esc_name);
|
free(esc_name);
|
||||||
|
if( date )
|
||||||
|
free(date);
|
||||||
|
if( cam )
|
||||||
|
free(cam);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,7 +864,13 @@ GetVideoMetadata(const char * path, char * name)
|
|||||||
DPRINTF(E_WARN, L_METADATA, "Unhandled format: %s\n", ctx->iformat->name);
|
DPRINTF(E_WARN, L_METADATA, "Unhandled format: %s\n", ctx->iformat->name);
|
||||||
}
|
}
|
||||||
av_close_input_file(ctx);
|
av_close_input_file(ctx);
|
||||||
|
#ifdef TIVO_SUPPORT
|
||||||
|
if( ends_with(path, ".TiVo") && is_tivo_file(path) )
|
||||||
|
{
|
||||||
|
free(m.mime);
|
||||||
|
asprintf(&m.mime, "video/x-tivo-mpeg");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||||
" (PATH, SIZE, DURATION, DATE, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
|
" (PATH, SIZE, DURATION, DATE, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
|
||||||
" TITLE, DLNA_PN, MIME) "
|
" TITLE, DLNA_PN, MIME) "
|
||||||
|
176
scanner.c
176
scanner.c
@ -52,6 +52,9 @@ is_video(const char * file)
|
|||||||
ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
|
ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
|
||||||
ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
|
ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
|
||||||
ends_with(file, ".vob") || ends_with(file, ".ts") ||
|
ends_with(file, ".vob") || ends_with(file, ".ts") ||
|
||||||
|
#ifdef TIVO_SUPPORT
|
||||||
|
ends_with(file, ".TiVo") ||
|
||||||
|
#endif
|
||||||
ends_with(file, ".flv") || ends_with(file, ".xvid"));
|
ends_with(file, ".flv") || ends_with(file, ".xvid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,88 +150,96 @@ insert_container(const char * item, const char * rootParent, const char * refID,
|
|||||||
void
|
void
|
||||||
insert_containers(const char * name, const char *path, const char * refID, const char * class, long unsigned int detailID)
|
insert_containers(const char * name, const char *path, const char * refID, const char * class, long unsigned int detailID)
|
||||||
{
|
{
|
||||||
char sql_buf[128];
|
|
||||||
char *sql;
|
char *sql;
|
||||||
char **result;
|
char **result;
|
||||||
int ret;
|
int ret;
|
||||||
int cols, row;
|
int cols, row;
|
||||||
long long int container;
|
sqlite_int64 container;
|
||||||
|
|
||||||
sprintf(sql_buf, "SELECT * from DETAILS where ID = %lu", detailID);
|
|
||||||
ret = sql_get_table(db, sql_buf, &result, &row, &cols);
|
|
||||||
|
|
||||||
if( strstr(class, "imageItem") )
|
if( strstr(class, "imageItem") )
|
||||||
{
|
{
|
||||||
char *date = result[13+cols], *cam = result[16+cols];
|
char *date = NULL, *cam = NULL;
|
||||||
char date_taken[11];
|
char date_taken[13], camera[64];
|
||||||
static struct virtual_item last_date;
|
static struct virtual_item last_date;
|
||||||
static struct virtual_item last_cam;
|
static struct virtual_item last_cam;
|
||||||
static struct virtual_item last_camdate;
|
static struct virtual_item last_camdate;
|
||||||
static sqlite_int64 last_all_objectID = 0;
|
static sqlite_int64 last_all_objectID = 0;
|
||||||
|
|
||||||
|
asprintf(&sql, "SELECT DATE, CREATOR from DETAILS where ID = %lu", detailID);
|
||||||
|
ret = sql_get_table(db, sql, &result, &row, &cols);
|
||||||
|
free(sql);
|
||||||
|
if( ret == SQLITE_OK )
|
||||||
|
{
|
||||||
|
date = result[2];
|
||||||
|
cam = result[3];
|
||||||
|
}
|
||||||
|
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
if( *date == '0' )
|
strncpy(date_taken, date, 10);
|
||||||
{
|
date_taken[10] = '\0';
|
||||||
strcpy(date_taken, "Unknown");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strncpy(date_taken, date, 10);
|
|
||||||
date_taken[10] = '\0';
|
|
||||||
}
|
|
||||||
if( strcmp(last_date.name, date_taken) == 0 )
|
|
||||||
{
|
|
||||||
last_date.objectID++;
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
container = insert_container(date_taken, "3$12", NULL, "album.photoAlbum", NULL, NULL, NULL, NULL);
|
|
||||||
sprintf(last_date.parentID, "3$12$%llX", container>>32);
|
|
||||||
last_date.objectID = (int)container;
|
|
||||||
strcpy(last_date.name, date_taken);
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
|
|
||||||
}
|
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
|
||||||
"VALUES"
|
|
||||||
" ('%s$%X', '%s', '%s', '%s', %lu, %Q)",
|
|
||||||
last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name);
|
|
||||||
sql_exec(db, sql);
|
|
||||||
sqlite3_free(sql);
|
|
||||||
|
|
||||||
if( cam )
|
|
||||||
{
|
|
||||||
if( strcmp(cam, last_cam.name) != 0 )
|
|
||||||
{
|
|
||||||
container = insert_container(cam, "3$13", NULL, "storageFolder", NULL, NULL, NULL, NULL);
|
|
||||||
sprintf(last_cam.parentID, "3$13$%llX", container>>32);
|
|
||||||
strncpy(last_cam.name, cam, 255);
|
|
||||||
last_camdate.name[0] = '\0';
|
|
||||||
}
|
|
||||||
if( strcmp(last_camdate.name, date_taken) == 0 )
|
|
||||||
{
|
|
||||||
last_camdate.objectID++;
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last camdate item: %s/%s/%s/%X\n", cam, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
container = insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL, NULL);
|
|
||||||
sprintf(last_camdate.parentID, "%s$%llX", last_cam.parentID, container>>32);
|
|
||||||
last_camdate.objectID = (int)container;
|
|
||||||
strcpy(last_camdate.name, date_taken);
|
|
||||||
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached camdate item: %s/%s/%s/%X\n", cam, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
|
|
||||||
}
|
|
||||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
|
||||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
|
||||||
"VALUES"
|
|
||||||
" ('%s$%X', '%s', '%s', '%s', %lu, %Q)",
|
|
||||||
last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name);
|
|
||||||
sql_exec(db, sql);
|
|
||||||
sqlite3_free(sql);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(date_taken, "Unknown Date");
|
||||||
|
}
|
||||||
|
if( strcmp(last_date.name, date_taken) == 0 )
|
||||||
|
{
|
||||||
|
last_date.objectID++;
|
||||||
|
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
container = insert_container(date_taken, "3$12", NULL, "album.photoAlbum", NULL, NULL, NULL, NULL);
|
||||||
|
sprintf(last_date.parentID, "3$12$%llX", container>>32);
|
||||||
|
last_date.objectID = (int)container;
|
||||||
|
strcpy(last_date.name, date_taken);
|
||||||
|
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
|
||||||
|
}
|
||||||
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
|
"VALUES"
|
||||||
|
" ('%s$%X', '%s', '%s', '%s', %lu, %Q)",
|
||||||
|
last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name);
|
||||||
|
sql_exec(db, sql);
|
||||||
|
sqlite3_free(sql);
|
||||||
|
|
||||||
|
if( cam )
|
||||||
|
{
|
||||||
|
strncpy(camera, cam, 63);
|
||||||
|
camera[63] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(camera, "Unknown Camera");
|
||||||
|
}
|
||||||
|
if( strcmp(camera, last_cam.name) != 0 )
|
||||||
|
{
|
||||||
|
container = insert_container(camera, "3$13", NULL, "storageFolder", NULL, NULL, NULL, NULL);
|
||||||
|
sprintf(last_cam.parentID, "3$13$%llX", container>>32);
|
||||||
|
strncpy(last_cam.name, camera, 255);
|
||||||
|
last_camdate.name[0] = '\0';
|
||||||
|
}
|
||||||
|
if( strcmp(last_camdate.name, date_taken) == 0 )
|
||||||
|
{
|
||||||
|
last_camdate.objectID++;
|
||||||
|
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
container = insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL, NULL);
|
||||||
|
sprintf(last_camdate.parentID, "%s$%llX", last_cam.parentID, container>>32);
|
||||||
|
last_camdate.objectID = (int)container;
|
||||||
|
strcpy(last_camdate.name, date_taken);
|
||||||
|
//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
|
||||||
|
}
|
||||||
|
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||||
|
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||||
|
"VALUES"
|
||||||
|
" ('%s$%X', '%s', '%s', '%s', %lu, %Q)",
|
||||||
|
last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name);
|
||||||
|
sql_exec(db, sql);
|
||||||
|
sqlite3_free(sql);
|
||||||
/* All Images */
|
/* All Images */
|
||||||
if( !last_all_objectID )
|
if( !last_all_objectID )
|
||||||
{
|
{
|
||||||
@ -244,8 +255,18 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
}
|
}
|
||||||
else if( strstr(class, "audioItem") )
|
else if( strstr(class, "audioItem") )
|
||||||
{
|
{
|
||||||
char *artist = cols ? result[7+cols]:NULL, *album = cols ? result[8+cols]:NULL, *genre = cols ? result[9+cols]:NULL;
|
asprintf(&sql, "SELECT ARTIST, ALBUM, GENRE, ALBUM_ART, ART_DLNA_PN from DETAILS where ID = %lu", detailID);
|
||||||
char *album_art = cols ? result[19+cols]:NULL, *art_dlna_pn = cols ? result[20+cols]:NULL;
|
ret = sql_get_table(db, sql, &result, &row, &cols);
|
||||||
|
free(sql);
|
||||||
|
if( ret != SQLITE_OK )
|
||||||
|
return;
|
||||||
|
if( !row )
|
||||||
|
{
|
||||||
|
sqlite3_free_table(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *artist = result[5], *album = result[6], *genre = result[7];
|
||||||
|
char *album_art = result[8], *art_dlna_pn = result[9];
|
||||||
static struct virtual_item last_album;
|
static struct virtual_item last_album;
|
||||||
static struct virtual_item last_artist;
|
static struct virtual_item last_artist;
|
||||||
static struct virtual_item last_artistAlbum;
|
static struct virtual_item last_artistAlbum;
|
||||||
@ -287,7 +308,7 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
strcpy(last_artist.name, artist);
|
strcpy(last_artist.name, artist);
|
||||||
last_artistAlbum.name[0] = '\0';
|
last_artistAlbum.name[0] = '\0';
|
||||||
/* Add this file to the "- All Albums -" container as well */
|
/* Add this file to the "- All Albums -" container as well */
|
||||||
container = insert_container("- All Albums -", last_artist.parentID, NULL, "storageFolder", NULL, genre, NULL, NULL);
|
container = insert_container("- All Albums -", last_artist.parentID, NULL, "storageFolder", artist, genre, NULL, NULL);
|
||||||
sprintf(last_artistAlbumAll.parentID, "%s$%llX", last_artist.parentID, container>>32);
|
sprintf(last_artistAlbumAll.parentID, "%s$%llX", last_artist.parentID, container>>32);
|
||||||
last_artistAlbumAll.objectID = (int)container;
|
last_artistAlbumAll.objectID = (int)container;
|
||||||
}
|
}
|
||||||
@ -396,6 +417,11 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
|||||||
last_all_objectID++, refID, class, detailID, name);
|
last_all_objectID++, refID, class, detailID, name);
|
||||||
sql_exec(db, sql);
|
sql_exec(db, sql);
|
||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_free_table(result);
|
sqlite3_free_table(result);
|
||||||
}
|
}
|
||||||
@ -637,7 +663,7 @@ CreateDatabase(void)
|
|||||||
if( ret != SQLITE_OK )
|
if( ret != SQLITE_OK )
|
||||||
goto sql_failed;
|
goto sql_failed;
|
||||||
ret = sql_exec(db, "CREATE TABLE SETTINGS ("
|
ret = sql_exec(db, "CREATE TABLE SETTINGS ("
|
||||||
"UPDATE_ID INTEGER PRIMARY KEY"
|
"UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0"
|
||||||
")");
|
")");
|
||||||
if( ret != SQLITE_OK )
|
if( ret != SQLITE_OK )
|
||||||
goto sql_failed;
|
goto sql_failed;
|
||||||
@ -762,10 +788,7 @@ ScanDirectory(const char * dir, const char * parent, enum media_types type)
|
|||||||
|
|
||||||
for (i=0; i < n; i++) {
|
for (i=0; i < n; i++) {
|
||||||
sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
|
sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
|
||||||
if( index(namelist[i]->d_name, '&') )
|
name = escape_tag(namelist[i]->d_name);
|
||||||
{
|
|
||||||
name = modifyString(strdup(namelist[i]->d_name), "&", "&amp;", 0);
|
|
||||||
}
|
|
||||||
if( namelist[i]->d_type == DT_DIR )
|
if( namelist[i]->d_type == DT_DIR )
|
||||||
{
|
{
|
||||||
insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID);
|
insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID);
|
||||||
@ -778,10 +801,7 @@ ScanDirectory(const char * dir, const char * parent, enum media_types type)
|
|||||||
fileno++;
|
fileno++;
|
||||||
}
|
}
|
||||||
if( name )
|
if( name )
|
||||||
{
|
|
||||||
free(name);
|
free(name);
|
||||||
name = NULL;
|
|
||||||
}
|
|
||||||
free(namelist[i]);
|
free(namelist[i]);
|
||||||
}
|
}
|
||||||
free(namelist);
|
free(namelist);
|
||||||
|
@ -696,7 +696,7 @@ _get_mp3fileinfo(char *file, struct song_metadata *psong)
|
|||||||
index++;
|
index++;
|
||||||
if(first_check)
|
if(first_check)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_SCANNER, "Bad header... dropping back for full frame search\n");
|
DPRINTF(E_INFO, L_SCANNER, "Bad header... dropping back for full frame search [%s]\n", psong->path);
|
||||||
first_check = 0;
|
first_check = 0;
|
||||||
fp_size = 0;
|
fp_size = 0;
|
||||||
break;
|
break;
|
||||||
|
296
tivo_commands.c
296
tivo_commands.c
@ -42,7 +42,7 @@ SendRootContainer(struct upnphttp * h)
|
|||||||
"<ContentType>x-container/tivo-server</ContentType>"
|
"<ContentType>x-container/tivo-server</ContentType>"
|
||||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||||
"<TotalDuration>0</TotalDuration>"
|
"<TotalDuration>0</TotalDuration>"
|
||||||
"<TotalItems>2</TotalItems>"
|
"<TotalItems>3</TotalItems>"
|
||||||
"<Title>%s</Title>"
|
"<Title>%s</Title>"
|
||||||
"</Details>"
|
"</Details>"
|
||||||
"<ItemStart>0</ItemStart>"
|
"<ItemStart>0</ItemStart>"
|
||||||
@ -71,22 +71,49 @@ SendRootContainer(struct upnphttp * h)
|
|||||||
"</Content>"
|
"</Content>"
|
||||||
"</Links>"
|
"</Links>"
|
||||||
"</Item>"
|
"</Item>"
|
||||||
"</TiVoContainer>", friendly_name, friendly_name, friendly_name);
|
"<Item>"
|
||||||
|
"<Details>"
|
||||||
|
"<ContentType>x-container/tivo-videos</ContentType>"
|
||||||
|
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||||
|
"<Title>Videos on %s</Title>"
|
||||||
|
"</Details>"
|
||||||
|
"<Links>"
|
||||||
|
"<Content>"
|
||||||
|
"<Url>/TiVoConnect?Command=QueryContainer&Container=2</Url>"
|
||||||
|
"<ContentType>x-container/tivo-videos</ContentType>"
|
||||||
|
"</Content>"
|
||||||
|
"</Links>"
|
||||||
|
"</Item>"
|
||||||
|
"</TiVoContainer>", friendly_name, friendly_name, friendly_name, friendly_name);
|
||||||
BuildResp_upnphttp(h, resp, len);
|
BuildResp_upnphttp(h, resp, len);
|
||||||
free(resp);
|
free(resp);
|
||||||
SendResp_upnphttp(h);
|
SendResp_upnphttp(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
unescape_tag(char * tag)
|
||||||
|
{
|
||||||
|
modifyString(tag, "&amp;", "&", 0);
|
||||||
|
modifyString(tag, "&amp;lt;", "<", 0);
|
||||||
|
modifyString(tag, "&lt;", "<", 0);
|
||||||
|
modifyString(tag, "&amp;gt;", ">", 0);
|
||||||
|
modifyString(tag, "&gt;", ">", 0);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FLAG_SEND_RESIZED 0x01
|
||||||
|
#define FLAG_NO_PARAMS 0x02
|
||||||
|
#define FLAG_VIDEO 0x04
|
||||||
int callback(void *args, int argc, char **argv, char **azColName)
|
int callback(void *args, int argc, char **argv, char **azColName)
|
||||||
{
|
{
|
||||||
struct Response *passed_args = (struct Response *)args;
|
struct Response *passed_args = (struct Response *)args;
|
||||||
char *id = argv[0], *class = argv[1], *detailID = argv[2], *size = argv[3], *title = argv[4], *duration = argv[5],
|
char *id = argv[0], *class = argv[1], *detailID = argv[2], *size = argv[3], *title = argv[4], *duration = argv[5],
|
||||||
*bitrate = argv[6], *sampleFrequency = argv[7], *artist = argv[8], *album = argv[9], *genre = argv[10],
|
*bitrate = argv[6], *sampleFrequency = argv[7], *artist = argv[8], *album = argv[9], *genre = argv[10],
|
||||||
*comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14];
|
*comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14], *path = argv[15];
|
||||||
char str_buf[4096];
|
char str_buf[4096];
|
||||||
char **result;
|
char **result;
|
||||||
int is_audio = 0;
|
int flags = 0;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
passed_args->total++;
|
passed_args->total++;
|
||||||
if( passed_args->start >= passed_args->total )
|
if( passed_args->start >= passed_args->total )
|
||||||
@ -96,101 +123,167 @@ int callback(void *args, int argc, char **argv, char **azColName)
|
|||||||
|
|
||||||
if( strncmp(class, "item", 4) == 0 )
|
if( strncmp(class, "item", 4) == 0 )
|
||||||
{
|
{
|
||||||
|
unescape_tag(title);
|
||||||
if( strncmp(mime, "audio", 5) == 0 )
|
if( strncmp(mime, "audio", 5) == 0 )
|
||||||
{
|
{
|
||||||
sprintf(str_buf, "<Item><Details>"
|
flags |= FLAG_NO_PARAMS;
|
||||||
"<ContentType>audio/*</ContentType>"
|
ret = sprintf(str_buf, "<Item><Details>"
|
||||||
"<SourceFormat>%s</SourceFormat>"
|
"<ContentType>audio/*</ContentType>"
|
||||||
"<SourceSize>%s</SourceSize>"
|
"<SourceFormat>%s</SourceFormat>"
|
||||||
"<SongTitle>%s</SongTitle>", mime, size, title);
|
"<SourceSize>%s</SourceSize>"
|
||||||
strcat(passed_args->resp, str_buf);
|
"<SongTitle>%s</SongTitle>", mime, size, title);
|
||||||
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
sprintf(str_buf, "<AlbumYear>%.*s</AlbumYear>", 4, date);
|
ret = sprintf(str_buf, "<AlbumYear>%.*s</AlbumYear>", 4, date);
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
is_audio = 1;
|
|
||||||
}
|
}
|
||||||
else if( strcmp(mime, "image/jpeg") == 0 )
|
else if( strcmp(mime, "image/jpeg") == 0 )
|
||||||
{
|
{
|
||||||
sprintf(str_buf, "<Item><Details>"
|
flags |= FLAG_SEND_RESIZED;
|
||||||
"<ContentType>image/*</ContentType>"
|
ret = sprintf(str_buf, "<Item><Details>"
|
||||||
"<SourceFormat>image/jpeg</SourceFormat>"
|
"<ContentType>image/*</ContentType>"
|
||||||
"<SourceSize>%s</SourceSize>", size);
|
"<SourceFormat>image/jpeg</SourceFormat>"
|
||||||
strcat(passed_args->resp, str_buf);
|
"<SourceSize>%s</SourceSize>", size);
|
||||||
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
if( date )
|
if( date )
|
||||||
{
|
{
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
memset(&tm, 0, sizeof(tm));
|
memset(&tm, 0, sizeof(tm));
|
||||||
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
||||||
sprintf(str_buf, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
ret = sprintf(str_buf, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( comment ) {
|
if( comment ) {
|
||||||
sprintf(str_buf, "<Caption>%s</Caption>", comment);
|
ret = sprintf(str_buf, "<Caption>%s</Caption>", comment);
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( strncmp(mime, "video", 5) == 0 )
|
||||||
|
{
|
||||||
|
flags |= FLAG_NO_PARAMS;
|
||||||
|
flags |= FLAG_VIDEO;
|
||||||
|
ret = sprintf(str_buf, "<Item><Details>"
|
||||||
|
"<ContentType>video/x-tivo-mpeg</ContentType>"
|
||||||
|
"<SourceFormat>%s</SourceFormat>"
|
||||||
|
"<SourceSize>%s</SourceSize>"
|
||||||
|
"<EpisodeTitle>%s</EpisodeTitle>", mime, size, title);
|
||||||
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
|
if( date )
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
memset(&tm, 0, sizeof(tm));
|
||||||
|
strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
||||||
|
ret = sprintf(str_buf, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
|
||||||
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
sprintf(str_buf, "<Title>%s</Title>", modifyString(title, "&amp;", "&", 0));
|
ret = sprintf(str_buf, "<Title>%s</Title>", unescape_tag(title));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
if( artist ) {
|
if( artist ) {
|
||||||
sprintf(str_buf, "<ArtistName>%s</ArtistName>", modifyString(artist, "&amp;", "&", 0));
|
ret = sprintf(str_buf, "<ArtistName>%s</ArtistName>", unescape_tag(artist));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( album ) {
|
if( album ) {
|
||||||
sprintf(str_buf, "<AlbumTitle>%s</AlbumTitle>", modifyString(album, "&amp;", "&", 0));
|
ret = sprintf(str_buf, "<AlbumTitle>%s</AlbumTitle>", unescape_tag(album));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( genre ) {
|
if( genre ) {
|
||||||
sprintf(str_buf, "<MusicGenre>%s</MusicGenre>", modifyString(genre, "&amp;", "&", 0));
|
ret = sprintf(str_buf, "<MusicGenre>%s</MusicGenre>", unescape_tag(genre));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( resolution ) {
|
if( resolution ) {
|
||||||
sprintf(str_buf, "<SourceWidth>%.*s</SourceWidth>"
|
ret = sprintf(str_buf, "<SourceWidth>%.*s</SourceWidth>"
|
||||||
"<SourceHeight>%s</SourceHeight>",
|
"<SourceHeight>%s</SourceHeight>",
|
||||||
(index(resolution, 'x')-resolution), resolution, (rindex(resolution, 'x')+1));
|
(index(resolution, 'x')-resolution), resolution, (rindex(resolution, 'x')+1));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( duration ) {
|
if( duration ) {
|
||||||
sprintf(str_buf, "<Duration>%d</Duration>",
|
ret = sprintf(str_buf, "<Duration>%d</Duration>",
|
||||||
atoi(rindex(duration, '.')+1) + (1000*atoi(rindex(duration, ':')+1)) + (60000*atoi(rindex(duration, ':')-2)) + (3600000*atoi(duration)));
|
atoi(rindex(duration, '.')+1) + (1000*atoi(rindex(duration, ':')+1)) + (60000*atoi(rindex(duration, ':')-2)) + (3600000*atoi(duration)));
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( bitrate ) {
|
if( bitrate ) {
|
||||||
sprintf(str_buf, "<SourceBitRate>%s</SourceBitRate>", bitrate);
|
ret = sprintf(str_buf, "<SourceBitRate>%s</SourceBitRate>", bitrate);
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
}
|
}
|
||||||
if( sampleFrequency ) {
|
if( sampleFrequency ) {
|
||||||
sprintf(str_buf, "<SourceSampleRate>%s</SourceSampleRate>", sampleFrequency);
|
ret = sprintf(str_buf, "<SourceSampleRate>%s</SourceSampleRate>", sampleFrequency);
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
|
}
|
||||||
|
ret = sprintf(str_buf, "</Details><Links><Content>"
|
||||||
|
"<ContentType>%s</ContentType>"
|
||||||
|
"<Url>/%s/%s.dat</Url>%s</Content>",
|
||||||
|
mime,
|
||||||
|
(flags & FLAG_SEND_RESIZED)?"Resized":"MediaItems", detailID,
|
||||||
|
(flags & FLAG_NO_PARAMS)?"<AcceptsParams>No</AcceptsParams>":"");
|
||||||
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
|
if( flags & FLAG_VIDEO )
|
||||||
|
{
|
||||||
|
char *name = basename(path);
|
||||||
|
char *esc_name = escape_tag(name);
|
||||||
|
ret = sprintf(str_buf, "<CustomIcon>"
|
||||||
|
"<ContentType>video/*</ContentType>"
|
||||||
|
"<Url>urn:tivo:image:save-until-i-delete-recording</Url>"
|
||||||
|
"</CustomIcon>"
|
||||||
|
"<Push><Container>Videos</Container></Push>"
|
||||||
|
"<File>%s</File> </Links>", esc_name?esc_name:name);
|
||||||
|
if( esc_name )
|
||||||
|
free(esc_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = sprintf(str_buf, "</Links>");
|
||||||
}
|
}
|
||||||
sprintf(str_buf, "</Details><Links><Content><Url>/%s/%s.dat</Url>%s</Content></Links>",
|
|
||||||
is_audio?"MediaItems":"Resized", detailID, is_audio?"<AcceptsParams>No</AcceptsParams>":"");
|
|
||||||
}
|
}
|
||||||
else if( strncmp(class, "container", 9) == 0 )
|
else if( strncmp(class, "container", 9) == 0 )
|
||||||
{
|
{
|
||||||
/* Determine the number of children */
|
/* Determine the number of children */
|
||||||
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
|
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
|
||||||
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
|
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
|
||||||
strcat(passed_args->resp, "<Item><Details>"
|
ret = sprintf(str_buf, "<Item>"
|
||||||
"<ContentType>x-container/folder</ContentType>"
|
"<Details>"
|
||||||
"<SourceFormat>x-container/folder</SourceFormat>");
|
"<ContentType>x-container/folder</ContentType>"
|
||||||
sprintf(str_buf, "<Title>%s</Title>"
|
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||||
"<TotalItems>%s</TotalItems>"
|
"<Title>%s</Title>"
|
||||||
"</Details>", modifyString(title, "&amp;", "&", 0), result[1]);
|
"<TotalItems>%s</TotalItems>"
|
||||||
strcat(passed_args->resp, str_buf);
|
"</Details>"
|
||||||
|
"<Links>"
|
||||||
sprintf(str_buf, "<Links><Content>"
|
"<Content>"
|
||||||
"<Url>/TiVoConnect?Command=QueryContainer&Container=%s</Url>"
|
"<Url>/TiVoConnect?Command=QueryContainer&Container=%s</Url>"
|
||||||
"</Content></Links>", id);
|
"<ContentType>x-tivo-container/folder</ContentType>"
|
||||||
|
"</Content>"
|
||||||
|
"</Links>",
|
||||||
|
unescape_tag(title), result[1], id);
|
||||||
sqlite3_free_table(result);
|
sqlite3_free_table(result);
|
||||||
}
|
}
|
||||||
strcat(passed_args->resp, str_buf);
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
strcat(passed_args->resp, "</Item>");
|
passed_args->size += ret;
|
||||||
|
ret = sprintf(str_buf, "</Item>");
|
||||||
|
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||||
|
passed_args->size += ret;
|
||||||
|
|
||||||
passed_args->returned++;
|
passed_args->returned++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -211,7 +304,7 @@ SendItemDetails(struct upnphttp * h, sqlite_int64 item)
|
|||||||
args.requested = 1;
|
args.requested = 1;
|
||||||
asprintf(&sql, "SELECT o.OBJECT_ID, o.CLASS, o.DETAIL_ID, d.SIZE, d.TITLE,"
|
asprintf(&sql, "SELECT o.OBJECT_ID, o.CLASS, o.DETAIL_ID, d.SIZE, d.TITLE,"
|
||||||
" d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM,"
|
" d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM,"
|
||||||
" d.GENRE, d.COMMENT, d.DATE, d.RESOLUTION, d.MIME "
|
" d.GENRE, d.COMMENT, d.DATE, d.RESOLUTION, d.MIME, d.PATH "
|
||||||
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
||||||
" where o.DETAIL_ID = %lld", item);
|
" where o.DETAIL_ID = %lld", item);
|
||||||
DPRINTF(E_DEBUG, L_TIVO, "%s\n", sql);
|
DPRINTF(E_DEBUG, L_TIVO, "%s\n", sql);
|
||||||
@ -233,30 +326,40 @@ void
|
|||||||
SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int itemCount, char * anchorItem,
|
SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int itemCount, char * anchorItem,
|
||||||
int anchorOffset, int recurse, char * sortOrder, char * filter, unsigned long int randomSeed)
|
int anchorOffset, int recurse, char * sortOrder, char * filter, unsigned long int randomSeed)
|
||||||
{
|
{
|
||||||
char * resp = malloc(1048576);
|
char * resp = malloc(262144);
|
||||||
char * items = malloc(1048576);
|
|
||||||
char *sql, *item, *saveptr;
|
char *sql, *item, *saveptr;
|
||||||
char *zErrMsg = NULL;
|
char *zErrMsg = NULL;
|
||||||
char **result;
|
char **result;
|
||||||
char *title;
|
char *title;
|
||||||
char what[10], order[64]={0}, order2[64]={0}, myfilter[128]={0};
|
char what[10], order[64]={0}, order2[64]={0}, myfilter[256]={0};
|
||||||
|
char str_buf[1024];
|
||||||
char *which;
|
char *which;
|
||||||
struct Response args;
|
struct Response args;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
*items = '\0';
|
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
|
memset(resp, 0, sizeof(262144));
|
||||||
|
|
||||||
args.resp = items;
|
args.resp = resp;
|
||||||
|
args.size = 1024;
|
||||||
args.requested = itemCount;
|
args.requested = itemCount;
|
||||||
|
|
||||||
if( strlen(objectID) == 1 )
|
if( strlen(objectID) == 1 )
|
||||||
{
|
{
|
||||||
if( *objectID == '1' )
|
switch( *objectID )
|
||||||
asprintf(&title, "Music on %s", friendly_name);
|
{
|
||||||
else if( *objectID == '3' )
|
case '1':
|
||||||
asprintf(&title, "Pictures on %s", friendly_name);
|
asprintf(&title, "Music on %s", friendly_name);
|
||||||
else
|
break;
|
||||||
asprintf(&title, "Unknown on %s", friendly_name);
|
case '2':
|
||||||
|
asprintf(&title, "Videos on %s", friendly_name);
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
asprintf(&title, "Pictures on %s", friendly_name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
asprintf(&title, "Unknown on %s", friendly_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -338,11 +441,11 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
unhandled_order:
|
unhandled_order:
|
||||||
item = strtok_r(NULL, ",", &saveptr);
|
item = strtok_r(NULL, ",", &saveptr);
|
||||||
}
|
}
|
||||||
strcat(order, "DETAIL_ID ASC");
|
strcat(order, "TITLE ASC, DETAIL_ID ASC");
|
||||||
if( itemCount >= 0 )
|
if( itemCount >= 0 )
|
||||||
strcat(order2, "DETAIL_ID ASC");
|
strcat(order2, "TITLE ASC, DETAIL_ID ASC");
|
||||||
else
|
else
|
||||||
strcat(order2, "DETAIL_ID DESC");
|
strcat(order2, "TITLE DESC, DETAIL_ID DESC");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -363,7 +466,8 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
{
|
{
|
||||||
strcat(myfilter, " or ");
|
strcat(myfilter, " or ");
|
||||||
}
|
}
|
||||||
if( strcasecmp(item, "x-container/folder") == 0 )
|
if( (strcasecmp(item, "x-container/folder") == 0) ||
|
||||||
|
(strncasecmp(item, "x-tivo-container/", 17) == 0) )
|
||||||
{
|
{
|
||||||
strcat(myfilter, "CLASS glob 'container*'");
|
strcat(myfilter, "CLASS glob 'container*'");
|
||||||
}
|
}
|
||||||
@ -375,6 +479,10 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
{
|
{
|
||||||
strcat(myfilter, "MIME = 'audio/mpeg'");
|
strcat(myfilter, "MIME = 'audio/mpeg'");
|
||||||
}
|
}
|
||||||
|
else if( strncasecmp(item, "video", 5) == 0 )
|
||||||
|
{
|
||||||
|
strcat(myfilter, "MIME = 'video/mpeg' or MIME = 'video/x-tivo-mpeg'");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DPRINTF(E_INFO, L_TIVO, "Unhandled Filter [%s]\n", item);
|
DPRINTF(E_INFO, L_TIVO, "Unhandled Filter [%s]\n", item);
|
||||||
@ -430,7 +538,7 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
sqlite3Prng.isInit = 0;
|
sqlite3Prng.isInit = 0;
|
||||||
sql = sqlite3_mprintf("SELECT o.OBJECT_ID, o.CLASS, o.DETAIL_ID, d.SIZE, d.TITLE,"
|
sql = sqlite3_mprintf("SELECT o.OBJECT_ID, o.CLASS, o.DETAIL_ID, d.SIZE, d.TITLE,"
|
||||||
" d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM,"
|
" d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM,"
|
||||||
" d.GENRE, d.COMMENT, d.DATE, d.RESOLUTION, d.MIME "
|
" d.GENRE, d.COMMENT, d.DATE, d.RESOLUTION, d.MIME, d.PATH "
|
||||||
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
||||||
" where %s and (%s)"
|
" where %s and (%s)"
|
||||||
" group by DETAIL_ID"
|
" group by DETAIL_ID"
|
||||||
@ -444,26 +552,27 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite
|
|||||||
sqlite3_free(zErrMsg);
|
sqlite3_free(zErrMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(resp, "<?xml version='1.0' encoding='UTF-8' ?>\n"
|
ret = sprintf(str_buf, "<?xml version='1.0' encoding='UTF-8' ?>\n"
|
||||||
"<TiVoContainer>"
|
"<TiVoContainer>"
|
||||||
"<Details>"
|
"<Details>"
|
||||||
"<ContentType>x-container/tivo-%s</ContentType>"
|
"<ContentType>x-container/tivo-%s</ContentType>"
|
||||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||||
"<TotalItems>%d</TotalItems>"
|
"<TotalItems>%d</TotalItems>"
|
||||||
"<Title>%s</Title>"
|
"<Title>%s</Title>"
|
||||||
"</Details>"
|
"</Details>"
|
||||||
"<ItemStart>%d</ItemStart>"
|
"<ItemStart>%d</ItemStart>"
|
||||||
"<ItemCount>%d</ItemCount>"
|
"<ItemCount>%d</ItemCount>",
|
||||||
"%s" /* the actual items xml */
|
(objectID[0]=='1' ? "music":"photos"),
|
||||||
"</TiVoContainer>",
|
args.total, title, args.start, args.returned);
|
||||||
(objectID[0]=='1' ? "music":"photos"),
|
args.resp = resp+1024-ret;
|
||||||
args.total,
|
memcpy(args.resp, &str_buf, ret);
|
||||||
title, args.start,
|
ret = sprintf(str_buf, "</TiVoContainer>");
|
||||||
args.returned, items);
|
memcpy(resp+args.size, &str_buf, ret+1);
|
||||||
free(items);
|
args.size += ret;
|
||||||
|
args.size -= args.resp-resp;
|
||||||
free(title);
|
free(title);
|
||||||
free(which);
|
free(which);
|
||||||
BuildResp_upnphttp(h, resp, strlen(resp));
|
BuildResp_upnphttp(h, args.resp, args.size);
|
||||||
free(resp);
|
free(resp);
|
||||||
SendResp_upnphttp(h);
|
SendResp_upnphttp(h);
|
||||||
}
|
}
|
||||||
@ -538,7 +647,8 @@ ProcessTiVoCommand(struct upnphttp * h, const char * orig_path)
|
|||||||
}
|
}
|
||||||
else if( strcasecmp(key, "Url") == 0 )
|
else if( strcasecmp(key, "Url") == 0 )
|
||||||
{
|
{
|
||||||
detailItem = strtoll(basename(val), NULL, 10);
|
if( val )
|
||||||
|
detailItem = strtoll(basename(val), NULL, 10);
|
||||||
}
|
}
|
||||||
else if( strcasecmp(key, "Format") == 0 )
|
else if( strcasecmp(key, "Format") == 0 )
|
||||||
{
|
{
|
||||||
|
18
utils.c
18
utils.c
@ -94,12 +94,28 @@ modifyString(char * string, const char * before, const char * after, short like)
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
escape_tag(const char *tag)
|
||||||
|
{
|
||||||
|
char *esc_tag = NULL;
|
||||||
|
|
||||||
|
if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') )
|
||||||
|
{
|
||||||
|
esc_tag = strdup(tag);
|
||||||
|
esc_tag = modifyString(esc_tag, "&", "&amp;", 0);
|
||||||
|
esc_tag = modifyString(esc_tag, "<", "&lt;", 0);
|
||||||
|
esc_tag = modifyString(esc_tag, ">", "&gt;", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return esc_tag;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
strip_ext(char * name)
|
strip_ext(char * name)
|
||||||
{
|
{
|
||||||
char * period;
|
char * period;
|
||||||
|
|
||||||
period = rindex(name, '.');
|
period = strrchr(name, '.');
|
||||||
if( period )
|
if( period )
|
||||||
*period = '\0';
|
*period = '\0';
|
||||||
}
|
}
|
||||||
|
3
utils.h
3
utils.h
@ -19,6 +19,9 @@ trim(char *str);
|
|||||||
char *
|
char *
|
||||||
modifyString(char * string, const char * before, const char * after, short like);
|
modifyString(char * string, const char * before, const char * after, short like);
|
||||||
|
|
||||||
|
char *
|
||||||
|
escape_tag(const char *tag);
|
||||||
|
|
||||||
void
|
void
|
||||||
strip_ext(char * name);
|
strip_ext(char * name);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user