X-Git-Url: https://code.octet-stream.net/broadcaster/blobdiff_plain/8320951221d45c5f5f3d387c5cb4b97d9fa2094c..33a19d553807d171f6ba9f4dafe30f43bc4bab5e:/internal/protocol/protocol.go diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go new file mode 100644 index 0000000..e23da3a --- /dev/null +++ b/internal/protocol/protocol.go @@ -0,0 +1,167 @@ +package protocol + +import ( + "encoding/json" + "errors" + "fmt" +) + +const ( + StartTimeFormat = "2006-01-02T15:04" + LocalTimeFormat = "Mon _2 Jan 2006 15:04:05" + + // Radio to server + + AuthenticateType = "authenticate" + StatusType = "status" + + // Server to radio + + FilesType = "files" + PlaylistsType = "playlists" + StopType = "stop" + + // Status values + + StatusIdle = "idle" + StatusDelay = "delay" + StatusChannelInUse = "channel_in_use" + StatusPlaying = "playing" +) + +// Base message type to determine what type of payload is expected. +type Message struct { + T string +} + +// Initial message from Radio to authenticate itself with a token string. +type AuthenticateMessage struct { + T string + Token string +} + +// Server updates the radio with the list of files that currently exist. +// This will be provided on connect and when there are any changes. +// The radio is expected to obtain all these files and cache them locally. +type FilesMessage struct { + T string + Files []FileSpec +} + +type PlaylistsMessage struct { + T string + Playlists []PlaylistSpec +} + +// Any playlist currently being played should be stopped and PTT disengaged. +type StopMessage struct { + T string +} + +type StatusMessage struct { + T string + + // Status w.r.t. playing a playlist + Status string + + // File being played or about to be played - empty string in idle status + Filename string + + // Name of playlist being played - empty string in idle status + Playlist string + + // Seconds until playback begins - never mind latency + DelaySecondsRemaining int + + // Number of seconds file has been actually playing + PlaybackSecondsElapsed int + + // Number of seconds waiting for channel to clear + WaitingForChannelSeconds int + + PTT bool + COS bool + FilesInSync bool + + // Timestamp of the current time on this radio, using LocalTimeFormat + LocalTime string + + // Time zone in use, e.g. "Australia/Hobart" + TimeZone string +} + +// Description of an individual file available in the broadcasting system. +type FileSpec struct { + // Filename, e.g. "broadcast.wav" + Name string + // SHA-256 hash of the file's contents + Hash string +} + +type PlaylistSpec struct { + Id int + Name string + StartTime string + Entries []EntrySpec +} + +type EntrySpec struct { + Filename string + DelaySeconds int + IsRelative bool +} + +func ParseMessage(data []byte) (string, interface{}, error) { + var t Message + err := json.Unmarshal(data, &t) + if err != nil { + return "", nil, err + } + + if t.T == AuthenticateType { + var auth AuthenticateMessage + err = json.Unmarshal(data, &auth) + if err != nil { + return "", nil, err + } + return t.T, auth, nil + } + + if t.T == FilesType { + var files FilesMessage + err = json.Unmarshal(data, &files) + if err != nil { + return "", nil, err + } + return t.T, files, nil + } + + if t.T == PlaylistsType { + var playlists PlaylistsMessage + err = json.Unmarshal(data, &playlists) + if err != nil { + return "", nil, err + } + return t.T, playlists, nil + } + + if t.T == StatusType { + var status StatusMessage + err = json.Unmarshal(data, &status) + if err != nil { + return "", nil, err + } + return t.T, status, nil + } + + if t.T == StopType { + var stop StopMessage + err = json.Unmarshal(data, &stop) + if err != nil { + return "", nil, err + } + return t.T, stop, nil + } + + return "", nil, errors.New(fmt.Sprintf("unknown message type %v", t.T)) +}