From dbffcb6dc02f90d1e3f7853832f749f89a9d2241 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Thu, 25 Jan 2024 19:59:43 +0000 Subject: [PATCH] Feature: Added support to send vorbis comments --- src/event_stream.c | 139 +++++++++++++++++++++++++++++++++++++++++++++ src/event_stream.h | 3 + src/format_mp3.c | 2 + src/format_ogg.c | 2 + 4 files changed, 146 insertions(+) diff --git a/src/event_stream.c b/src/event_stream.c index c03b07cb..e508373d 100644 --- a/src/event_stream.c +++ b/src/event_stream.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "common/thread/thread.h" @@ -33,7 +34,9 @@ #include "event.h" #include "client.h" #include "connection.h" +#include "source.h" #include "errors.h" +#include "format_mp3.h" #include "logging.h" #define CATMODULE "event-stream" @@ -44,10 +47,12 @@ struct event_stream_event_tag { const char * uuid; const char * mount; + const char * source_instance_uuid; const char * rendered; size_t rendered_length; event_t *event; + vorbis_comment *vc; event_stream_event_t *next; }; @@ -95,6 +100,8 @@ static void event_stream_event_free(igloo_ro_t self) { event_stream_event_t *event = igloo_ro_to_type(self, event_stream_event_t); igloo_sp_unref(&(event->uuid), igloo_instance); + igloo_sp_unref(&(event->mount), igloo_instance); + igloo_sp_unref(&(event->source_instance_uuid), igloo_instance); igloo_sp_unref(&(event->rendered), igloo_instance); igloo_ro_unref(&(event->event)); igloo_ro_unref(&(event->next)); @@ -255,6 +262,12 @@ void event_stream_add_client(client_t *client) fserve_add_client_callback(client, event_stream_add_client_inner, NULL); } +static void event_stream_set_source(event_stream_event_t *event, source_t *source) +{ + igloo_sp_replace(source->mount, &(event->mount), igloo_instance); + igloo_sp_replace(source->instance_uuid, &(event->source_instance_uuid), igloo_instance); +} + void event_stream_emit_event(event_t *event) { event_stream_event_t *el = event_stream_event_new(); @@ -267,6 +280,22 @@ void event_stream_emit_event(event_t *event) event_stream_queue(el); } +void event_stream_emit_vc(source_t *source, vorbis_comment *vc) +{ + event_stream_event_t *el = event_stream_event_new(); + if (!el) + return; + + event_stream_set_source(el, source); + + /* we only have a temp reference to vc, so we set it, render, and then unset before we queue. */ + el->vc = vc; + event_stream_event_render(el); + el->vc = NULL; + + event_stream_queue(el); +} + static void event_stream_send_to_client(client_t *client) { event_stream_clientstate_t *state = client->format_data; @@ -393,6 +422,8 @@ static void event_stream_event_render(event_stream_event_t *event) string_renderer_t * renderer; json_renderer_t *json; char *body; + bool has_type = false; + bool has_crude = false; if (event->rendered) return; @@ -409,6 +440,9 @@ static void event_stream_event_render(event_stream_event_t *event) if (event->event) { event_t *uevent = event->event; + has_type = true; + has_crude = true; + json_renderer_write_key(json, "type", JSON_RENDERER_FLAGS_NONE); json_renderer_write_string(json, "event", JSON_RENDERER_FLAGS_NONE); @@ -450,6 +484,111 @@ static void event_stream_event_render(event_stream_event_t *event) json_renderer_write_key(json, "mount", JSON_RENDERER_FLAGS_NONE); json_renderer_write_string(json, event->mount, JSON_RENDERER_FLAGS_NONE); } + + if (event->vc) { + if (!has_type) { + json_renderer_write_key(json, "type", JSON_RENDERER_FLAGS_NONE); + json_renderer_write_string(json, "vc", JSON_RENDERER_FLAGS_NONE); + has_type = true; + } + + json_renderer_write_key(json, "vc", JSON_RENDERER_FLAGS_NONE); + json_renderer_begin(json, JSON_ELEMENT_TYPE_OBJECT); + { + /* ok, this part is tricky, we first need to figure out all the keys. + * we however cheat here by assuming a few things. + * TODO: Fix this. + */ + struct { + char name[64]; + size_t count; + } keys[64]; + memset(keys, 0, sizeof(keys)); + + /* it is an int in libvorbis, not a size_t */ + for (int i = 0; i < event->vc->comments; i++) { + const char *comment = event->vc->user_comments[i]; + const char *keyend = strchr(comment, '='); + bool found = false; + size_t keylen; + + if (!keyend) + continue; + keylen = keyend - comment; + if (keylen >= sizeof(keys->name)) + continue; + + for (size_t j = 0; j < (sizeof(keys)/sizeof(*keys)); j++) { + char *name = keys[j].name; + + if (*name) { + if (strncasecmp(comment, name, keylen) == 0) { + found = true; + keys[j].count++; + } + } + } + + if (found) + break; + + for (size_t j = 0; j < (sizeof(keys)/sizeof(*keys)); j++) { + char *name = keys[j].name; + + if (!*name) { + memcpy(name, comment, keylen); + name[keylen] = 0; + keys[j].count = 1; + igloo_cs_to_upper(name); + break; + } + } + } + + /* Now we have a list of all keys... */ + for (size_t j = 0; j < (sizeof(keys)/sizeof(*keys)); j++) { + const char *name = keys[j].name; + if (!*name) + continue; + + json_renderer_write_key(json, name, JSON_RENDERER_FLAGS_NONE); + json_renderer_begin(json, JSON_ELEMENT_TYPE_ARRAY); + for (size_t i = 0; i < keys[j].count; i++) { + const char *value = vorbis_comment_query(event->vc, name, i); + json_renderer_write_string(json, value, JSON_RENDERER_FLAGS_NONE); + } + json_renderer_end(json); + } + } + json_renderer_end(json); + + if (!has_crude) { + has_crude = true; + json_renderer_write_key(json, "crude", JSON_RENDERER_FLAGS_NONE); + json_renderer_begin(json, JSON_ELEMENT_TYPE_OBJECT); + { + static const char * display_title_keys[] = {"TITLE", MP3_METADATA_TITLE}; + const char *display_title = NULL; + for (size_t i = 0; i < (sizeof(display_title_keys)/sizeof(*display_title_keys)); i++) { + display_title = vorbis_comment_query(event->vc, display_title_keys[i], 0); + if (display_title) + break; + } + json_renderer_write_key(json, "display-title", JSON_RENDERER_FLAGS_NONE); + if (display_title) { + json_renderer_write_string(json, display_title, JSON_RENDERER_FLAGS_NONE); + } else { + json_renderer_write_null(json); + } + if (event->source_instance_uuid) { + json_renderer_write_key(json, "source-instance", JSON_RENDERER_FLAGS_NONE); + json_renderer_write_string(json, event->source_instance_uuid, JSON_RENDERER_FLAGS_NONE); + } + } + json_renderer_end(json); + } + } + json_renderer_end(json); body = json_renderer_finish(&json); diff --git a/src/event_stream.h b/src/event_stream.h index f2ac6ffe..526892fb 100644 --- a/src/event_stream.h +++ b/src/event_stream.h @@ -9,6 +9,8 @@ #ifndef __EVENT_STREAM_H__ #define __EVENT_STREAM_H__ +#include + #include "icecasttypes.h" igloo_RO_FORWARD_TYPE(event_stream_event_t); @@ -18,5 +20,6 @@ void event_stream_shutdown(void); void event_stream_add_client(client_t *client); void event_stream_emit_event(event_t *event); +void event_stream_emit_vc(source_t *source, vorbis_comment *vc); #endif diff --git a/src/format_mp3.c b/src/format_mp3.c index 8af8178b..b7c62f83 100644 --- a/src/format_mp3.c +++ b/src/format_mp3.c @@ -39,6 +39,7 @@ #include "source.h" #include "client.h" #include "connection.h" +#include "event_stream.h" #include "stats.h" #include "format.h" @@ -206,6 +207,7 @@ static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigne yp_touch (source->mount); free (p); playlist_push_track(source->history, &source->format->vc); + event_stream_emit_vc(source, &source->format->vc); } } while (0); } diff --git a/src/format_ogg.c b/src/format_ogg.c index 3ffaecdd..ce595d62 100644 --- a/src/format_ogg.c +++ b/src/format_ogg.c @@ -37,6 +37,7 @@ #include "stats.h" #include "playlist.h" +#include "event_stream.h" #include "event.h" #include "format.h" #include "format_ogg.h" @@ -332,6 +333,7 @@ static void update_comments(source_t *source) stats_event (source->mount, "display-title", title); playlist_push_track(source->history, &source->format->vc); + event_stream_emit_vc(source, &source->format->vc); codec = ogg_info->codecs; while (codec)