* Use internal music metadata functions intead of taglib.
1) Taglib does not support MP4 or WMA/ASF without hacking it in there. 2) Taglib is C++, so it's nice to remove that dependency. * Use embedded album art where available.
This commit is contained in:
273
tivo_commands.c
273
tivo_commands.c
@ -1,5 +1,7 @@
|
||||
#include "config.h"
|
||||
#ifdef ENABLE_TIVO
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tivo_utils.h"
|
||||
@ -28,7 +30,248 @@ SendRootContainer(struct upnphttp * h)
|
||||
"<Details>"
|
||||
"<ContentType>x-container/tivo-photos</ContentType>"
|
||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||
"<Title>Pictures</Title>"
|
||||
"<Title>Pictures on %s</Title>"
|
||||
"</Details>"
|
||||
"<Links>"
|
||||
"<Content>"
|
||||
"<Url>/TiVoConnect?Command=QueryContainer&Container=3/Pictures on %s</Url>"
|
||||
"</Content>"
|
||||
"</Links>"
|
||||
"</Item>"
|
||||
"<Item>"
|
||||
"<Details>"
|
||||
"<ContentType>x-container/tivo-music</ContentType>"
|
||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||
"<Title>Music on %s</Title>"
|
||||
"</Details>"
|
||||
"<Links>"
|
||||
"<Content>"
|
||||
"<Url>/TiVoConnect?Command=QueryContainer&Container=1</Url>"
|
||||
"</Content>"
|
||||
"</Links>"
|
||||
"</Item>"
|
||||
"</TiVoContainer>", friendly_name, friendly_name, friendly_name, friendly_name);
|
||||
BuildResp_upnphttp(h, resp, len);
|
||||
SendResp_upnphttp(h);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int callback(void *args, int argc, char **argv, char **azColName)
|
||||
{
|
||||
struct Response *passed_args = (struct Response *)args;
|
||||
char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *detailID = argv[5], *size = argv[9], *title = argv[10],
|
||||
*duration = argv[11], *bitrate = argv[12], *sampleFrequency = argv[13], *artist = argv[14], *album = argv[15],
|
||||
*genre = argv[16], *comment = argv[17], *nrAudioChannels = argv[18], *track = argv[19], *date = argv[20], *resolution = argv[21],
|
||||
*tn = argv[22], *creator = argv[23], *dlna_pn = argv[24], *mime = argv[25], *album_art = argv[26], *art_dlna_pn = argv[27];
|
||||
char dlna_buf[64];
|
||||
char str_buf[4096];
|
||||
char **result;
|
||||
int ret;
|
||||
|
||||
passed_args->total++;
|
||||
|
||||
if( passed_args->requested && (passed_args->returned >= passed_args->requested) )
|
||||
return 0;
|
||||
passed_args->returned++;
|
||||
|
||||
if( dlna_pn )
|
||||
sprintf(dlna_buf, "DLNA.ORG_PN=%s", dlna_pn);
|
||||
else
|
||||
strcpy(dlna_buf, "*");
|
||||
|
||||
if( strncmp(class, "item", 4) == 0 )
|
||||
{
|
||||
if( passed_args->client == EXbox )
|
||||
{
|
||||
if( strcmp(mime, "video/divx") == 0 )
|
||||
{
|
||||
mime[6] = 'a';
|
||||
mime[7] = 'v';
|
||||
mime[8] = 'i';
|
||||
mime[9] = '\0';
|
||||
}
|
||||
}
|
||||
sprintf(str_buf, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( refID && (!passed_args->filter || strstr(passed_args->filter, "@refID")) ) {
|
||||
sprintf(str_buf, " refID=\"%s\"", refID);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
"<upnp:class>object.%s</upnp:class>",
|
||||
title, class);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) {
|
||||
sprintf(str_buf, "<dc:description>%s</dc:description>", comment);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
|
||||
sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( date && (!passed_args->filter || strstr(passed_args->filter, "dc:date")) ) {
|
||||
sprintf(str_buf, "<dc:date>%s</dc:date>", date);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
|
||||
sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( album && (!passed_args->filter || strstr(passed_args->filter, "upnp:album")) ) {
|
||||
sprintf(str_buf, "<upnp:album>%s</upnp:album>", album);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
|
||||
sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( track && atoi(track) && (!passed_args->filter || strstr(passed_args->filter, "upnp:originalTrackNumber")) ) {
|
||||
sprintf(str_buf, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
|
||||
strcat(passed_args->resp, "<upnp:albumArtURI ");
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) {
|
||||
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, ">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
lan_addr[0].str, runtime_vars.port, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
|
||||
strcat(passed_args->resp, "<res ");
|
||||
if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) {
|
||||
sprintf(str_buf, "size=\"%s\" ", size);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( duration && (!passed_args->filter || strstr(passed_args->filter, "res@duration")) ) {
|
||||
sprintf(str_buf, "duration=\"%s\" ", duration);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( bitrate && (!passed_args->filter || strstr(passed_args->filter, "res@bitrate")) ) {
|
||||
sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( sampleFrequency && (!passed_args->filter || strstr(passed_args->filter, "res@sampleFrequency")) ) {
|
||||
sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( nrAudioChannels && (!passed_args->filter || strstr(passed_args->filter, "res@nrAudioChannels")) ) {
|
||||
sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
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\">"
|
||||
"http://%s:%d/MediaItems/%s.dat"
|
||||
"</res>",
|
||||
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
|
||||
#if 0 //JPEG_RESIZE
|
||||
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
|
||||
strcat(passed_args->resp, str_buf);
|
||||
sprintf(str_buf, "<res "
|
||||
"protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/Resized/%s"
|
||||
"</res>",
|
||||
mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, runtime_vars.port, id);
|
||||
}
|
||||
#endif
|
||||
if( tn && atoi(tn) && dlna_pn ) {
|
||||
strcat(passed_args->resp, str_buf);
|
||||
strcat(passed_args->resp, "<res ");
|
||||
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/Thumbnails/%s.dat"
|
||||
"</res>",
|
||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
|
||||
}
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
strcpy(str_buf, "</item>");
|
||||
}
|
||||
else if( strncmp(class, "container", 9) == 0 )
|
||||
{
|
||||
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
|
||||
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
|
||||
sprintf(str_buf, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "@childCount"))
|
||||
{
|
||||
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, unless they're filtered out */
|
||||
if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
|
||||
{
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:searchClass") )
|
||||
{
|
||||
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>",
|
||||
title, class);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
|
||||
sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
|
||||
sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
|
||||
sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
|
||||
strcat(passed_args->resp, "<upnp:albumArtURI ");
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) {
|
||||
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, ">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
lan_addr[0].str, runtime_vars.port, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
sprintf(str_buf, "</container>");
|
||||
sqlite3_free_table(result);
|
||||
}
|
||||
strcat(passed_args->resp, str_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
SendContainer(struct upnphttp * h, const char * objectID, const char * title, int itemStart, int itemCount)
|
||||
{
|
||||
char * resp;
|
||||
int len;
|
||||
|
||||
len = asprintf(&resp, "<?xml version='1.0' encoding='UTF-8' ?>\n"
|
||||
"<TiVoContainer>"
|
||||
"<Details>"
|
||||
"<ContentType>x-container/tivo-%s</ContentType>"
|
||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||
"<TotalDuration>0</TotalDuration>"
|
||||
"<TotalItems>2</TotalItems>"
|
||||
"<Title>%s</Title>"
|
||||
"</Details>"
|
||||
"<ItemStart>%d</ItemStart>"
|
||||
"<ItemCount>%d</ItemCount>"
|
||||
"<Item>"
|
||||
"<Details>"
|
||||
"<ContentType>x-container/tivo-photos</ContentType>"
|
||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||
"<Title>Pictures on %s</Title>"
|
||||
"</Details>"
|
||||
"<Links>"
|
||||
"<Content>"
|
||||
@ -40,7 +283,7 @@ SendRootContainer(struct upnphttp * h)
|
||||
"<Details>"
|
||||
"<ContentType>x-container/tivo-music</ContentType>"
|
||||
"<SourceFormat>x-container/folder</SourceFormat>"
|
||||
"<Title>Music</Title>"
|
||||
"<Title>Music on %s</Title>"
|
||||
"</Details>"
|
||||
"<Links>"
|
||||
"<Content>"
|
||||
@ -48,7 +291,9 @@ SendRootContainer(struct upnphttp * h)
|
||||
"</Content>"
|
||||
"</Links>"
|
||||
"</Item>"
|
||||
"</TiVoContainer>", friendly_name);
|
||||
"</TiVoContainer>",
|
||||
(objectID[0]=='1') ? "music":"photos",
|
||||
title, itemStart, itemCount, title, title);
|
||||
BuildResp_upnphttp(h, resp, len);
|
||||
SendResp_upnphttp(h);
|
||||
}
|
||||
@ -60,6 +305,7 @@ ProcessTiVoCommand(struct upnphttp * h, const char * orig_path)
|
||||
char *key, *val;
|
||||
char *saveptr, *item;
|
||||
char *command = NULL, *container = NULL;
|
||||
int itemStart=0, itemCount=0;
|
||||
|
||||
path = decodeString(orig_path);
|
||||
DPRINTF(E_DEBUG, L_GENERAL, "Processing TiVo command %s\n", path);
|
||||
@ -83,12 +329,29 @@ ProcessTiVoCommand(struct upnphttp * h, const char * orig_path)
|
||||
{
|
||||
container = val;
|
||||
}
|
||||
else if( strcasecmp(key, "itemstart") == 0 )
|
||||
{
|
||||
itemStart = atoi(val);
|
||||
}
|
||||
else if( strcasecmp(key, "itemcount") == 0 )
|
||||
{
|
||||
itemCount = atoi(val);
|
||||
}
|
||||
item = strtok_r( NULL, "&", &saveptr );
|
||||
}
|
||||
|
||||
if( !container || (strcmp(container, "/") == 0) )
|
||||
if( strcmp(command, "QueryContainer") == 0 )
|
||||
{
|
||||
SendRootContainer(h);
|
||||
if( !container || (strcmp(container, "/") == 0) )
|
||||
{
|
||||
SendRootContainer(h);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = container;
|
||||
key = strsep(&val, "/");
|
||||
SendContainer(h, container, val, itemStart, itemCount);
|
||||
}
|
||||
}
|
||||
CloseSocket_upnphttp(h);
|
||||
}
|
||||
|
Reference in New Issue
Block a user