diff --git a/node/icecast/status.go b/node/icecast/status.go index 8a9a7717137abf4f5c936260ebd3a63de9097a1d..55151e5fd1089dd7a9aa10cbeebac0514de19485 100644 --- a/node/icecast/status.go +++ b/node/icecast/status.go @@ -21,22 +21,41 @@ var ( icecastStatusUpdateInterval = 2 * time.Second ) +// The JSON status document returned by Icecast does not really have a +// schema: the XSL adaptor will generate "plausible" JSON by guessing +// the data types. This means that if someone sets the current song +// title to, say, "1234", this will be encoded in the resulting JSON +// as a number, not a string, and the naive encoding/json +// deserialization to string will fail with a type error. +// +// Since this data collection is optimistic anyway, we create our own +// "tolerant string" type and eat those errors away. This is used on +// all fields that are ultimately user-controlled. +type maybeString string + +func (s *maybeString) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, s); err != nil { + *s = "" + } + return nil +} + // TODO: deserialize properly the time format used by Icecast. type icecastMountStatus struct { - Artist string `json:"artist"` - BitRate int32 `json:"audio_bitrate"` - Channels int32 `json:"audio_channels"` - AudioInfo string `json:"audio_info"` - SampleRate int32 `json:"audio_samplerate"` - Genre string `json:"genre"` - Listeners int32 `json:"listeners"` - ListenURL string `json:"listenurl"` - Quality string `json:"quality"` - Description string `json:"server_description"` - Name string `json:"server_name"` - Type string `json:"server_type"` - Subtype string `json:"subtype"` - Title string `json:"title"` + Artist maybeString `json:"artist"` + BitRate int32 `json:"audio_bitrate"` + Channels int32 `json:"audio_channels"` + AudioInfo string `json:"audio_info"` + SampleRate int32 `json:"audio_samplerate"` + Genre maybeString `json:"genre"` + Listeners int32 `json:"listeners"` + ListenURL string `json:"listenurl"` + Quality string `json:"quality"` + Description maybeString `json:"server_description"` + Name maybeString `json:"server_name"` + Type string `json:"server_type"` + Subtype string `json:"subtype"` + Title maybeString `json:"title"` //StreamStart time.Time `json:"stream_start_iso8601"` } @@ -101,10 +120,10 @@ func convertIcecastStatus(status []icecastMountStatus) []*pb.IcecastMount { BitRate: m.BitRate, SampleRate: m.SampleRate, Channels: m.Channels, - Name: m.Name, - Description: m.Description, - Title: m.Title, - Artist: m.Artist, + Name: string(m.Name), + Description: string(m.Description), + Title: string(m.Title), + Artist: string(m.Artist), } // For whatever reason, quality is a string in Icecast's JSON. if q, err := strconv.ParseFloat(m.Quality, 32); err == nil { diff --git a/proto/status.proto b/proto/status.proto index 8eb77e5e7bf1cb2a27152025268ba1ceac1dd2bb..c9a7af8da4720ae4072eaccdf559e84151c374ac 100644 --- a/proto/status.proto +++ b/proto/status.proto @@ -5,8 +5,6 @@ syntax = "proto3"; package autoradio; -//import "google/protobuf/empty.proto"; - message IcecastMount { string path = 1; int32 listeners = 2;