From 39b8fe4b2c864247e0004d2a8c0ca55bab0fdf32 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Fri, 6 Nov 2009 21:12:38 +0100 Subject: rewrote icy header parser shoutcast title is being read from icy headers --- deadbeef.h | 4 ++ plugins.c | 2 + plugins/mpgmad/mpgmad.c | 14 +++++ plugins/vfs_curl/vfs_curl.c | 137 +++++++++++++++++++++++++++++++++++++++----- vfs.c | 15 +++++ vfs.h | 2 + 6 files changed, 160 insertions(+), 14 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index b9cc4c5e..51f512dc 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -236,6 +236,8 @@ typedef struct { void (*rewind) (DB_FILE *stream); int64_t (*fgetlength) (DB_FILE *stream); const char *(*fget_content_type) (DB_FILE *stream); + const char *(*fget_content_name) (DB_FILE *stream); + const char *(*fget_content_genre) (DB_FILE *stream); // message passing int (*sendmessage) (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2); // configuration access @@ -393,6 +395,8 @@ typedef struct DB_vfs_s { void (*rewind) (DB_FILE *stream); int64_t (*getlength)(DB_FILE *stream); const char * (*get_content_type) (DB_FILE *stream); + const char * (*get_content_name) (DB_FILE *stream); + const char * (*get_content_genre) (DB_FILE *stream); const char **scheme_names; // NULL-terminated list of supported schemes, e.g. {"http", "ftp", NULL} unsigned streaming : 1; } DB_vfs_t; diff --git a/plugins.c b/plugins.c index 53604178..3ed508e5 100644 --- a/plugins.c +++ b/plugins.c @@ -111,6 +111,8 @@ static DB_functions_t deadbeef_api = { .rewind = vfs_rewind, .fgetlength = vfs_fgetlength, .fget_content_type = vfs_get_content_type, + .fget_content_name = vfs_get_content_name, + .fget_content_genre = vfs_get_content_genre, // message passing .sendmessage = messagepump_push, // configuration access diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c index 21d3b8fb..8e54b6c8 100644 --- a/plugins/mpgmad/mpgmad.c +++ b/plugins/mpgmad/mpgmad.c @@ -498,6 +498,8 @@ cmp3_init (DB_playItem_t *it) { } else { int len = deadbeef->fgetlength (buffer.file); + const char *name = deadbeef->fget_content_name (buffer.file); + const char *genre = deadbeef->fget_content_genre (buffer.file); if (len > 0) { deadbeef->pl_delete_all_meta (it); int v2err = deadbeef->junk_read_id3v2 (it, buffer.file); @@ -506,6 +508,18 @@ cmp3_init (DB_playItem_t *it) { deadbeef->fseek (buffer.file, 0, SEEK_SET); } } + else { + deadbeef->pl_delete_all_meta (it); + if (name) { + deadbeef->pl_add_meta (it, "title", name); + } + else { + deadbeef->pl_add_meta (it, "title", NULL); + } + if (genre) { + deadbeef->pl_add_meta (it, "genre", genre); + } + } int res = cmp3_scan_stream (&buffer, 0); if (res < 0) { trace ("mpgmad: cmp3_init: initial cmp3_scan_stream failed\n"); diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c index e467692e..1ac15c93 100644 --- a/plugins/vfs_curl/vfs_curl.c +++ b/plugins/vfs_curl/vfs_curl.c @@ -58,6 +58,8 @@ typedef struct { int icyheader; int nheaderpackets; char *content_type; + char *content_name; + char *content_genre; uint8_t status; } HTTP_FILE; @@ -170,28 +172,91 @@ http_size_header_handler (void *ptr, size_t size, size_t nmemb, void *stream) { return size * nmemb; } +static const uint8_t * +parse_header (const uint8_t *p, const uint8_t *e, uint8_t *key, int keysize, uint8_t *value, int valuesize) { + int sz; // will hold lenght of extracted string + const uint8_t *v; // pointer to current character + keysize--; + valuesize--; + *key = 0; + *value = 0; + v = p; + // find : + while (v < e && *v != 0x0d && *v != 0x0a && *v != ':') { + v++; + } + if (*v != ':') { + // skip linebreaks + while (v < e && (*v == 0x0d || *v == 0x0a)) { + v++; + } + return v; + } + // copy key + sz = v-p; + sz = min (keysize, sz); + memcpy (key, p, sz); + key[sz] = 0; + + // skip whitespace + v++; + while (v < e && (*v == 0x20 || *v == 0x08)) { + v++; + } + if (*v == 0x0d || *v == 0x0a) { + // skip linebreaks + while (v < e && (*v == 0x0d || *v == 0x0a)) { + v++; + } + return v; + } + p = v; + + // find linebreak + while (v < e && *v != 0x0d || *v == 0x0a) { + v++; + } + + // copy value + sz = v-p; + sz = min (valuesize, sz); + memcpy (value, p, sz); + value[sz] = 0; + + // skip linebreaks + while (v < e && (*v == 0x0d || *v == 0x0a)) { + v++; + } + return v; +} + static size_t http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream) { trace ("http_content_header_handler\n"); assert (stream); HTTP_FILE *fp = (HTTP_FILE *)stream; const uint8_t c_type_str[] ="Content-Type:"; - const uint8_t *cl = strcasestr (ptr, c_type_str); - if (cl) { - fp->gotheader = 1; - cl += sizeof (c_type_str)-1; - while (*cl <= 0x20) { - cl++; + const uint8_t icy_name_str[] ="icy-name:"; + const uint8_t icy_genre_str[] ="icy-genre:"; + const uint8_t *p = ptr; + const uint8_t *end = p + size*nmemb; + uint8_t key[256]; + uint8_t value[256]; + while (p < end) { + p = parse_header (p, end, key, sizeof (key), value, sizeof (value)); + trace ("%skey=%s value=%s\n", fp->icyheader ? "[icy] " : "", key, value); + if (!strcasecmp (key, "Content-Type")) { + fp->content_type = strdup (value); + } + else if (!strcasecmp (key, "icy-name")) { + fp->content_name = strdup (value); } - const uint8_t *p = (const uint8_t *)cl; - while (*p >= 0x20) { - p++; + else if (!strcasecmp (key, "icy-genre")) { + fp->content_genre = strdup (value); } - int len = (const uint8_t *)p-cl; - char str[len+1]; - strncpy (str, cl, len); - str[len] = 0; - fp->content_type = strdup (str); + } + if (!fp->icyheader) { + fp->gotheader = 1; } return size * nmemb; } @@ -300,6 +365,12 @@ http_close (DB_FILE *stream) { if (fp->content_type) { free (fp->content_type); } + if (fp->content_name) { + free (fp->content_name); + } + if (fp->content_genre) { + free (fp->content_genre); + } if (fp->url) { free (fp->url); } @@ -476,6 +547,42 @@ http_get_content_type (DB_FILE *stream) { return fp->content_type; } +static const char * +http_get_content_name (DB_FILE *stream) { + trace ("http_get_content_name\n"); + assert (stream); + HTTP_FILE *fp = (HTTP_FILE *)stream; + if (fp->gotheader) { + return fp->content_name; + } + if (!fp->tid) { + http_start_streamer (fp); + } + trace ("http_get_content_name waiting for response...\n"); + while (fp->status != STATUS_FINISHED && fp->status != STATUS_ABORTED && !fp->gotheader) { + usleep (3000); + } + return fp->content_name; +} + +static const char * +http_get_content_genre (DB_FILE *stream) { + trace ("http_get_content_genre\n"); + assert (stream); + HTTP_FILE *fp = (HTTP_FILE *)stream; + if (fp->gotheader) { + return fp->content_genre; + } + if (!fp->tid) { + http_start_streamer (fp); + } + trace ("http_get_content_genre waiting for response...\n"); + while (fp->status != STATUS_FINISHED && fp->status != STATUS_ABORTED && !fp->gotheader) { + usleep (3000); + } + return fp->content_genre; +} + static const char *scheme_names[] = { "http://", "ftp://", NULL }; // standard stdio vfs @@ -497,6 +604,8 @@ static DB_vfs_t plugin = { .rewind = http_rewind, .getlength = http_getlength, .get_content_type = http_get_content_type, + .get_content_name = http_get_content_name, + .get_content_genre = http_get_content_type, .scheme_names = scheme_names, .streaming = 1 }; diff --git a/vfs.c b/vfs.c index 7e6f64ee..2c038fac 100644 --- a/vfs.c +++ b/vfs.c @@ -84,3 +84,18 @@ const char * vfs_get_content_type (DB_FILE *stream) { return stream->vfs->get_content_type (stream); } + +const char * +vfs_get_content_name (DB_FILE *stream) { + if (stream->vfs->get_content_name) { + return stream->vfs->get_content_name (stream); + } + return NULL; +} +const char * +vfs_get_content_genre (DB_FILE *stream) { + if (stream->vfs->get_content_genre) { + return stream->vfs->get_content_genre (stream); + } + return NULL; +} diff --git a/vfs.h b/vfs.h index 6bb7afd7..599e63e4 100644 --- a/vfs.h +++ b/vfs.h @@ -30,5 +30,7 @@ int64_t vfs_ftell (DB_FILE *stream); void vfs_rewind (DB_FILE *stream); int64_t vfs_fgetlength (DB_FILE *stream); const char *vfs_get_content_type (DB_FILE *stream); +const char *vfs_get_content_name (DB_FILE *stream); +const char *vfs_get_content_genre (DB_FILE *stream); #endif // __VFS_H -- cgit v1.2.3