Issue description:
Metadata updates performed on an icecast2 ogg/flac stream created in liquidsoap are ignored in Symfonium and metadata updates seem to crash playback.
Logs:
655321 liquidsoap icecast.log
Upload description:
655321 - Liquidsoap embedded metadata support for icecast radio stream
Additional information:
I’ve recently revisited this topic where I tried to add metadata available via an API to an online radio stream that lacked this information.
I have achieved this outside of Symfonium by using the original stream as a source, parsing the API response and adding the metadata to a new version of the stream. I chose ogg/flac because the source stream is MP3 320KBit/s and I dislike transcoding lossy formats to other lossy formats. Flac bloats the size but leads to no further audio degradation and in my home network, ~1MBit/s of traffic is irrelevant.
On the off-chance that Tolriq is familiar with liquidsoup, here’s the code I use to achieve this. Don’t expect it to be good, I wanted to slam my head against a wall while writing it many, many times.
bbc6music.liq
# IceCast credentials
icecast_host = "192.168.1.53"
icecast_port = 8000
icecast_user = "source"
icecast_pass = "REDACTED"
mount_point = "/bbc6music.ogg"
# BBC Radio 6 Music stream URL
bbc_stream = "http://as-hls-ww-live.akamaized.net/pool_81827798/live/ww/bbc_6music/bbc_6music.isml/bbc_6music-audio%3D320000.norewind.m3u8"
# API url to fetch the current metadata
#api_url = "https://rms.api.bbc.co.uk/v2/services/bbc_6music/segments/latest"
api_url = "https://radio-metadata.fr/api?id=bbcradio6"
# Fetch audio stream
radio = mksafe(input.http(bbc_stream, clock_safe=true))
# Wrap the source with insert_metadata to enable metadata updates
radio = insert_metadata(radio)
def update_metadata()
def get_api()
api_response = process.read.lines("curl -s #{api_url}")
json_string = list.hd(api_response) # Get the first line of JSON
json_string
end
def parse_api_response(json_string)
let json.parse ( x : {
mode: string,
data: {
metadata: string,
title: string?,
artist: string?,
cover: string
},
}) = json_string
x
end
json_string = get_api()
mdata = parse_api_response(json_string)
# Set empty values to "unknown" as they are ignored by lastfm
title = if null.defined(mdata.data.title) then mdata.data.title else "unknown" end
artist = if null.defined(mdata.data.artist) then mdata.data.artist else "unknown" end
cover = string.replace(pattern="800x800", (fun(_) -> "raw"), mdata.data.cover)
if mdata.mode == "song" then
# Log what's playing
log("Playing #{mdata.data.title} by #{mdata.data.artist}")
else
log("Not playing music, currently listening to #{mdata.data.metadata}.")
end
# Insert metadata into the radio stream
radio.insert_metadata([
("title", string_of(title)),
("artist", string_of(artist)),
("coverart", cover)
])
end
thread.run(update_metadata, every=30.)
# Stream to Icecast using FLAC
output.icecast(
%ogg(%flac(samplerate=48000, channels=2, compression=8, bits_per_sample=16)),
host=icecast_host,
port=icecast_port,
user=icecast_user,
password=icecast_pass,
mount=mount_point,
radio
)
The resulting new stream can be played back in
- MusicBee, which detects title and artist, not cover url
- vlc, which detects title, artist and cover url (no display)
- foobar2000, which detects title and artist, not cover url
- mpc-hc, which doesn’t seem to detect any of the tags
- mpv, which only detects the title
Symfonium uses none of the embedded tags, but they are detected in the logs:
2025-03-31 03:15:05.701 Verbose/TranscoderManager: FFProbe [790ms]: {"streams":[{"index":0,"codec_name":"flac","codec_long_name":"unknown","codec_type":"audio","codec_tag_string":"[0][0][0][0]","codec_tag":"0x0000","sample_fmt":"s16","sample_rate":"48000","channels":2,"channel_layout":"stereo","bits_per_sample":0,"initial_padding":0,"r_frame_rate":"0\/0","avg_frame_rate":"0\/0","time_base":"1\/48000","start_pts":0,"start_time":"0.000000","bits_per_raw_sample":"16","extradata_size":34,"disposition":{"default":0,"dub":0,"original":0,"comment":0,"lyrics":0,"karaoke":0,"forced":0,"hearing_impaired":0,"visual_impaired":0,"clean_effects":0,"attached_pic":0,"timed_thumbnails":0,"captions":0,"descriptions":0,"metadata":0,"dependent":0,"still_image":0},"tags":{"coverart":"https:\/\/ichef.bbci.co.uk\/images\/ic\/raw\/p01bqmgz.jpg","artist":"The Seperate","title":"This Night Has Opened My Eyes (feat. Joan As Police Woman)"}}],"format":{"filename":"https:\/\/192.168.1.53\/bbc6music.ogg","nb_streams":1,"nb_programs":0,"format_name":"ogg","start_time":"0.000000","probe_score":100,"tags":{"icy-name":"\/bbc6music.ogg","icy-pub":"1"}}}
this part of the line, to be exact:
"tags":{"coverart":"https:\/\/ichef.bbci.co.uk\/images\/ic\/raw\/p01bqmgz.jpg","artist":"The Seperate","title":"This Night Has Opened My Eyes (feat. Joan As Police Woman)"}
Screenshots:
Musicbee metadata:
VLC metadata:
Foobar2000 metadata:

mpv metadata:
Symfonium sadly displays none of the metadata (ignore the dinosaur):
And it aborts playback in under 30s each time I start it. Considering that the metadata update happens every 30s, I’d guess that Symfonium doesn’t like the changing metadata.
It would be really cool if Symfonium could utilize the metadata I added to the icecast stream via liquidsoup (at least title and artist, I don’t know how hard updating the current artwork from an url to the image would be) and also if it could play the stream without crashing within 30s.
(I was tempted to put a sun emoji here).