diff --git a/metadata.c b/metadata.c
index ef4225c..f2347dd 100644
--- a/metadata.c
+++ b/metadata.c
@@ -255,7 +255,17 @@ parse_nfo(const char *path, metadata_t *m)
val = GetValueFromNameValueList(&xml, "genre");
if( val )
+ {
+ free(m->genre);
m->genre = strdup(val);
+ }
+
+ val = GetValueFromNameValueList(&xml, "mime");
+ if( val )
+ {
+ free(m->mime);
+ m->mime = strdup(val);
+ }
ClearNameValueList(&xml);
fclose(nfo);
@@ -758,24 +768,6 @@ GetVideoMetadata(const char *path, char *name)
return 0;
}
- strcpy(nfo, path);
- ext = strrchr(nfo, '.');
- if( ext )
- {
- strcpy(ext+1, "nfo");
- if( access(nfo, F_OK) == 0 )
- {
- parse_nfo(nfo, &m);
- }
- }
-
- if( !m.date )
- {
- m.date = malloc(20);
- modtime = localtime(&file.st_mtime);
- strftime(m.date, 20, "%FT%T", modtime);
- }
-
if( ac )
{
aac_object_type_t aac_type = AAC_INVALID;
@@ -1483,26 +1475,6 @@ GetVideoMetadata(const char *path, char *name)
break;
}
}
- if( !m.mime )
- {
- if( strcmp(ctx->iformat->name, "avi") == 0 )
- xasprintf(&m.mime, "video/x-msvideo");
- else if( strncmp(ctx->iformat->name, "mpeg", 4) == 0 )
- xasprintf(&m.mime, "video/mpeg");
- else if( strcmp(ctx->iformat->name, "asf") == 0 )
- xasprintf(&m.mime, "video/x-ms-wmv");
- else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
- if( ends_with(path, ".mov") )
- xasprintf(&m.mime, "video/quicktime");
- else
- xasprintf(&m.mime, "video/mp4");
- else if( strncmp(ctx->iformat->name, "matroska", 8) == 0 )
- xasprintf(&m.mime, "video/x-matroska");
- else if( strcmp(ctx->iformat->name, "flv") == 0 )
- xasprintf(&m.mime, "video/x-flv");
- else
- DPRINTF(E_WARN, L_METADATA, "%s: Unhandled format: %s\n", path, ctx->iformat->name);
- }
if( strcmp(ctx->iformat->name, "asf") == 0 )
{
@@ -1557,7 +1529,6 @@ GetVideoMetadata(const char *path, char *name)
#endif
#endif
video_no_dlna:
- lav_close(ctx);
#ifdef TIVO_SUPPORT
if( ends_with(path, ".TiVo") && is_tivo_file(path) )
@@ -1567,10 +1538,51 @@ video_no_dlna:
free(m.dlna_pn);
m.dlna_pn = NULL;
}
- m.mime = realloc(m.mime, 18);
+ m.mime = realloc(m.mime, 21);
strcpy(m.mime, "video/x-tivo-mpeg");
}
#endif
+
+ strcpy(nfo, path);
+ ext = strrchr(nfo, '.');
+ if( ext )
+ {
+ strcpy(ext+1, "nfo");
+ if( access(nfo, F_OK) == 0 )
+ {
+ parse_nfo(nfo, &m);
+ }
+ }
+
+ if( !m.mime )
+ {
+ if( strcmp(ctx->iformat->name, "avi") == 0 )
+ xasprintf(&m.mime, "video/x-msvideo");
+ else if( strncmp(ctx->iformat->name, "mpeg", 4) == 0 )
+ xasprintf(&m.mime, "video/mpeg");
+ else if( strcmp(ctx->iformat->name, "asf") == 0 )
+ xasprintf(&m.mime, "video/x-ms-wmv");
+ else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
+ if( ends_with(path, ".mov") )
+ xasprintf(&m.mime, "video/quicktime");
+ else
+ xasprintf(&m.mime, "video/mp4");
+ else if( strncmp(ctx->iformat->name, "matroska", 8) == 0 )
+ xasprintf(&m.mime, "video/x-matroska");
+ else if( strcmp(ctx->iformat->name, "flv") == 0 )
+ xasprintf(&m.mime, "video/x-flv");
+ else
+ DPRINTF(E_WARN, L_METADATA, "%s: Unhandled format: %s\n", path, ctx->iformat->name);
+ }
+ lav_close(ctx);
+
+ if( !m.date )
+ {
+ m.date = malloc(20);
+ modtime = localtime(&file.st_mtime);
+ strftime(m.date, 20, "%FT%T", modtime);
+ }
+
if( !m.title )
m.title = strdup(name);
diff --git a/tivo_commands.c b/tivo_commands.c
index 8b7d97c..792341b 100644
--- a/tivo_commands.c
+++ b/tivo_commands.c
@@ -93,6 +93,28 @@ SendRootContainer(struct upnphttp *h)
SendResp_upnphttp(h);
}
+static void
+SendFormats(struct upnphttp *h, const char *sformat)
+{
+ char *resp;
+ int len;
+
+ len = xasprintf(&resp, ""
+ ""
+ ""
+ "video/x-tivo-mpeg"
+ ""
+ ""
+ ""
+ "%s"
+ ""
+ ""
+ "", sformat);
+ BuildResp_upnphttp(h, resp, len);
+ free(resp);
+ SendResp_upnphttp(h);
+}
+
static char *
unescape_tag(char *tag)
{
@@ -113,7 +135,7 @@ callback(void *args, int argc, char **argv, char **azColName)
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],
*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], *path = argv[15];
+ *comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14];
struct string_s *str = passed_args->str;
if( strncmp(class, "item", 4) == 0 )
@@ -124,22 +146,22 @@ callback(void *args, int argc, char **argv, char **azColName)
{
flags |= FLAG_NO_PARAMS;
strcatf(str, ""
- "audio/*"
+ "%s"
"%s"
- "%s"
- "%s", mime, size, title);
+ "%s",
+ "audio/*", mime, size);
+ strcatf(str, "%s", title);
if( date )
- {
strcatf(str, "%.*s", 4, date);
- }
}
else if( strcmp(mime, "image/jpeg") == 0 )
{
flags |= FLAG_SEND_RESIZED;
strcatf(str, ""
- "image/*"
- "image/jpeg"
- "%s", size);
+ "%s"
+ "%s"
+ "%s",
+ "image/*", mime, size);
if( date )
{
struct tm tm;
@@ -149,19 +171,17 @@ callback(void *args, int argc, char **argv, char **azColName)
strcatf(str, "0x%X", (unsigned int)mktime(&tm));
}
if( comment )
- {
strcatf(str, "%s", comment);
- }
}
else if( strncmp(mime, "video", 5) == 0 )
{
char *episode;
- flags |= FLAG_NO_PARAMS;
flags |= FLAG_VIDEO;
strcatf(str, ""
- "video/x-tivo-mpeg"
+ "%s"
"%s"
- "%s", mime, size);
+ "%s",
+ mime, mime, size);
episode = strstr(title, " - ");
if( episode )
{
@@ -182,9 +202,7 @@ callback(void *args, int argc, char **argv, char **azColName)
strcatf(str, "0x%X", (unsigned int)mktime(&tm));
}
if( comment )
- {
strcatf(str, "%s", comment);
- }
}
else
{
@@ -217,27 +235,23 @@ callback(void *args, int argc, char **argv, char **azColName)
if( sampleFrequency ) {
strcatf(str, "%s", sampleFrequency);
}
- strcatf(str, " "
- "%s"
- "/%s/%s.dat%s",
+ strcatf(str, " "
+ ""
+ "%s"
+ "/%s/%s.%s%s"
+ "",
mime,
- (flags & FLAG_SEND_RESIZED)?"Resized":"MediaItems", detailID,
- (flags & FLAG_NO_PARAMS)?"No":"");
+ (flags & FLAG_SEND_RESIZED) ? "Resized" : "MediaItems",
+ detailID, mime_to_ext(mime),
+ (flags & FLAG_NO_PARAMS) ? "No" : "");
if( flags & FLAG_VIDEO )
{
- char *esc_name = escape_tag(basename(path), 1);
strcatf(str, ""
- "video/*"
+ "image/*"
"urn:tivo:image:save-until-i-delete-recording"
- ""
- "Videos"
- "%s ", esc_name);
- free(esc_name);
- }
- else
- {
- strcatf(str, "");
+ "");
}
+ strcatf(str, "");
}
else if( strncmp(class, "container", 9) == 0 )
{
@@ -247,7 +261,7 @@ callback(void *args, int argc, char **argv, char **azColName)
count = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", id);
#else
count = sql_get_int_field(db, "SELECT count(*) from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where PARENT_ID = '%s' and "
- " (MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg')"
+ " (MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts')"
" or CLASS glob 'container*')", id);
#endif
strcatf(str, "- "
@@ -274,7 +288,7 @@ callback(void *args, int argc, char **argv, char **azColName)
#define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.CLASS, o.DETAIL_ID, d.SIZE, d.TITLE," \
" d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM, d.GENRE," \
- " d.COMMENT, d.DATE, d.RESOLUTION, d.MIME, d.PATH, d.DISC, d.TRACK "
+ " d.COMMENT, d.DATE, d.RESOLUTION, d.MIME, d.DISC, d.TRACK "
static void
SendItemDetails(struct upnphttp *h, int64_t item)
@@ -534,7 +548,7 @@ SendContainer(struct upnphttp *h, const char *objectID, int itemStart, int itemC
}
else if( strncasecmp(item, "video", 5) == 0 )
{
- strcat(myfilter, "MIME in ('video/mpeg', 'video/x-tivo-mpeg')");
+ strcat(myfilter, "MIME in ('video/mpeg', 'video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts')");
}
else
{
@@ -551,7 +565,7 @@ SendContainer(struct upnphttp *h, const char *objectID, int itemStart, int itemC
}
else
{
- strcpy(myfilter, "MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg') or CLASS glob 'container*'");
+ strcpy(myfilter, "MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts') or CLASS glob 'container*'");
}
if( anchorItem )
@@ -652,7 +666,7 @@ ProcessTiVoCommand(struct upnphttp *h, const char *orig_path)
char *key, *val;
char *saveptr = NULL, *item;
char *command = NULL, *container = NULL, *anchorItem = NULL;
- char *sortOrder = NULL, *filter = NULL;
+ char *sortOrder = NULL, *filter = NULL, *sformat = NULL;
int64_t detailItem=0;
int itemStart=0, itemCount=-100, anchorOffset=0, recurse=0;
unsigned long int randomSeed=0;
@@ -718,6 +732,10 @@ ProcessTiVoCommand(struct upnphttp *h, const char *orig_path)
if( val )
detailItem = strtoll(basename(val), NULL, 10);
}
+ else if( strcasecmp(key, "SourceFormat") == 0 )
+ {
+ sformat = val;
+ }
else if( strcasecmp(key, "Format") == 0 || // Only send XML
strcasecmp(key, "SerialNum") == 0 || // Unused for now
strcasecmp(key, "DoGenres") == 0 ) // Not sure what this is, so ignore it
@@ -752,6 +770,10 @@ ProcessTiVoCommand(struct upnphttp *h, const char *orig_path)
{
SendItemDetails(h, detailItem);
}
+ else if( strcmp(command, "QueryFormats") == 0 )
+ {
+ SendFormats(h, sformat);
+ }
else
{
DPRINTF(E_DEBUG, L_GENERAL, "Unhandled command [%s]\n", command);
diff --git a/upnpsoap.c b/upnpsoap.c
index 89fb764..a6fb866 100644
--- a/upnpsoap.c
+++ b/upnpsoap.c
@@ -292,78 +292,6 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
ClearNameValueList(&data);
}
-static void
-mime_to_ext(const char * mime, char * buf)
-{
- switch( *mime )
- {
- /* Audio extensions */
- case 'a':
- if( strcmp(mime+6, "mpeg") == 0 )
- strcpy(buf, "mp3");
- else if( strcmp(mime+6, "mp4") == 0 )
- strcpy(buf, "m4a");
- else if( strcmp(mime+6, "x-ms-wma") == 0 )
- strcpy(buf, "wma");
- else if( strcmp(mime+6, "x-flac") == 0 )
- strcpy(buf, "flac");
- else if( strcmp(mime+6, "flac") == 0 )
- strcpy(buf, "flac");
- else if( strcmp(mime+6, "x-wav") == 0 )
- strcpy(buf, "wav");
- else if( strncmp(mime+6, "L16", 3) == 0 )
- strcpy(buf, "pcm");
- else if( strcmp(mime+6, "3gpp") == 0 )
- strcpy(buf, "3gp");
- else if( strcmp(mime, "application/ogg") == 0 )
- strcpy(buf, "ogg");
- else
- strcpy(buf, "dat");
- break;
- case 'v':
- if( strcmp(mime+6, "avi") == 0 )
- strcpy(buf, "avi");
- else if( strcmp(mime+6, "divx") == 0 )
- strcpy(buf, "avi");
- else if( strcmp(mime+6, "x-msvideo") == 0 )
- strcpy(buf, "avi");
- else if( strcmp(mime+6, "mpeg") == 0 )
- strcpy(buf, "mpg");
- else if( strcmp(mime+6, "mp4") == 0 )
- strcpy(buf, "mp4");
- else if( strcmp(mime+6, "x-ms-wmv") == 0 )
- strcpy(buf, "wmv");
- else if( strcmp(mime+6, "x-matroska") == 0 )
- strcpy(buf, "mkv");
- else if( strcmp(mime+6, "x-mkv") == 0 )
- strcpy(buf, "mkv");
- else if( strcmp(mime+6, "x-flv") == 0 )
- strcpy(buf, "flv");
- else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
- strcpy(buf, "mpg");
- else if( strcmp(mime+6, "quicktime") == 0 )
- strcpy(buf, "mov");
- else if( strcmp(mime+6, "3gpp") == 0 )
- strcpy(buf, "3gp");
- else if( strcmp(mime+6, "x-tivo-mpeg") == 0 )
- strcpy(buf, "TiVo");
- else
- strcpy(buf, "dat");
- break;
- case 'i':
- if( strcmp(mime+6, "jpeg") == 0 )
- strcpy(buf, "jpg");
- else if( strcmp(mime+6, "png") == 0 )
- strcpy(buf, "png");
- else
- strcpy(buf, "dat");
- break;
- default:
- strcpy(buf, "dat");
- break;
- }
-}
-
/* Standard DLNA/UPnP filter flags */
#define FILTER_CHILDCOUNT 0x00000001
#define FILTER_DC_CREATOR 0x00000002
@@ -671,7 +599,7 @@ add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn,
inline static void
add_res(char *size, char *duration, char *bitrate, char *sampleFrequency,
char *nrAudioChannels, char *resolution, char *dlna_pn, char *mime,
- char *detailID, char *ext, struct Response *args)
+ char *detailID, const char *ext, struct Response *args)
{
strcatf(args->str, "<res ");
if( size && (args->filter & FILTER_RES_SIZE) ) {
@@ -728,7 +656,7 @@ callback(void *args, int argc, char **argv, char **azColName)
*genre = argv[12], *comment = argv[13], *nrAudioChannels = argv[14], *track = argv[15], *date = argv[16], *resolution = argv[17],
*tn = argv[18], *creator = argv[19], *dlna_pn = argv[20], *mime = argv[21], *album_art = argv[22];
char dlna_buf[128];
- char ext[5];
+ const char *ext;
struct string_s *str = passed_args->str;
int ret = 0;
@@ -938,7 +866,7 @@ callback(void *args, int argc, char **argv, char **azColName)
}
}
if( passed_args->filter & FILTER_RES ) {
- mime_to_ext(mime, ext);
+ ext = mime_to_ext(mime);
add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
resolution, dlna_buf, mime, detailID, ext, passed_args);
if( *mime == 'i' ) {
diff --git a/utils.c b/utils.c
index b9079c2..c2cb5eb 100644
--- a/utils.c
+++ b/utils.c
@@ -293,6 +293,72 @@ DJBHash(const char *str, int len)
return hash;
}
+const char *
+mime_to_ext(const char * mime)
+{
+ switch( *mime )
+ {
+ /* Audio extensions */
+ case 'a':
+ if( strcmp(mime+6, "mpeg") == 0 )
+ return "mp3";
+ else if( strcmp(mime+6, "mp4") == 0 )
+ return "m4a";
+ else if( strcmp(mime+6, "x-ms-wma") == 0 )
+ return "wma";
+ else if( strcmp(mime+6, "x-flac") == 0 )
+ return "flac";
+ else if( strcmp(mime+6, "flac") == 0 )
+ return "flac";
+ else if( strcmp(mime+6, "x-wav") == 0 )
+ return "wav";
+ else if( strncmp(mime+6, "L16", 3) == 0 )
+ return "pcm";
+ else if( strcmp(mime+6, "3gpp") == 0 )
+ return "3gp";
+ else if( strcmp(mime, "application/ogg") == 0 )
+ return "ogg";
+ break;
+ case 'v':
+ if( strcmp(mime+6, "avi") == 0 )
+ return "avi";
+ else if( strcmp(mime+6, "divx") == 0 )
+ return "avi";
+ else if( strcmp(mime+6, "x-msvideo") == 0 )
+ return "avi";
+ else if( strcmp(mime+6, "mpeg") == 0 )
+ return "mpg";
+ else if( strcmp(mime+6, "mp4") == 0 )
+ return "mp4";
+ else if( strcmp(mime+6, "x-ms-wmv") == 0 )
+ return "wmv";
+ else if( strcmp(mime+6, "x-matroska") == 0 )
+ return "mkv";
+ else if( strcmp(mime+6, "x-mkv") == 0 )
+ return "mkv";
+ else if( strcmp(mime+6, "x-flv") == 0 )
+ return "flv";
+ else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
+ return "mpg";
+ else if( strcmp(mime+6, "quicktime") == 0 )
+ return "mov";
+ else if( strcmp(mime+6, "3gpp") == 0 )
+ return "3gp";
+ else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 )
+ return "TiVo";
+ break;
+ case 'i':
+ if( strcmp(mime+6, "jpeg") == 0 )
+ return "jpg";
+ else if( strcmp(mime+6, "png") == 0 )
+ return "png";
+ break;
+ default:
+ break;
+ }
+ return "dat";
+}
+
int
is_video(const char * file)
{
diff --git a/utils.h b/utils.h
index 191e133..d9d49a4 100644
--- a/utils.h
+++ b/utils.h
@@ -26,58 +26,29 @@
#include "minidlnatypes.h"
-int
-strcatf(struct string_s *str, char *fmt, ...);
+/* String functions */
+int strcatf(struct string_s *str, char *fmt, ...);
+void strncpyt(char *dst, const char *src, size_t len);
+inline int xasprintf(char **strp, char *fmt, ...);
+int ends_with(const char * haystack, const char * needle);
+char *trim(char *str);
+char *strstrc(const char *s, const char *p, const char t);
+char *strcasestrc(const char *s, const char *p, const char t);
+char *modifyString(char * string, const char * before, const char * after);
+char *escape_tag(const char *tag, int force_alloc);
+void strip_ext(char * name);
-void
-strncpyt(char *dst, const char *src, size_t len);
+/* Metadata functions */
+int is_video(const char * file);
+int is_audio(const char * file);
+int is_image(const char * file);
+int is_playlist(const char * file);
+int is_album_art(const char * name);
+int resolve_unknown_type(const char * path, media_types dir_type);
+const char *mime_to_ext(const char * mime);
-inline int
-xasprintf(char **strp, char *fmt, ...);
-
-int
-ends_with(const char * haystack, const char * needle);
-
-char *
-trim(char *str);
-
-char *
-strstrc(const char *s, const char *p, const char t);
-
-char *
-strcasestrc(const char *s, const char *p, const char t);
-
-char *
-modifyString(char * string, const char * before, const char * after);
-
-char *
-escape_tag(const char *tag, int force_alloc);
-
-void
-strip_ext(char * name);
-
-int
-make_dir(char * path, mode_t mode);
-
-unsigned int
-DJBHash(const char *str, int len);
-
-int
-is_video(const char * file);
-
-int
-is_audio(const char * file);
-
-int
-is_image(const char * file);
-
-int
-is_playlist(const char * file);
-
-int
-is_album_art(const char * name);
-
-int
-resolve_unknown_type(const char * path, media_types dir_type);
+/* Others */
+int make_dir(char * path, mode_t mode);
+unsigned int DJBHash(const char *str, int len);
#endif