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