package radio import ( "bytes" "errors" "strconv" "time" ) const MostListenedDateFormat = "02 January 2006" var mlsChanged = false type MostListenedSong struct { Listeners int Date time.Time Song string } func (mls *MostListenedSong) DateString() string { return mls.Date.Format(MostListenedDateFormat) } var mostListened MostListenedSong // CheckAndUpdateMostListenedSong compares current most played song with // provided `cur`rent song's listeners, and if it is larger, then it takes // `prev`ious song's name. // // Why we take a previous song's name? Experimentally I noticed that Icecast // writes amount of listeners that was by the very start of a next song. So // it means that it was actually amount of listeners by the end of // the previous song. // // So it would be fairer to give these listeners back to a song they was // listening to. func CheckAndUpdateMostListenedSong(cur, prev Song) { if prev.Song == "" { return } l, _ := strconv.Atoi(cur.Listeners) if l > mostListened.Listeners { mostListened = MostListenedSong{ Listeners: l, Date: time.Now().UTC(), Song: prev.Song} } mlsChanged = true } // MostListened returns song that currently is the song with most simultaneous // listeners. func MostListened() *MostListenedSong { return &mostListened } func LoadMostListenedSong(data []byte) (err error) { lines := bytes.Split(data, []byte{'\n'}) if len(lines) != 3 { return errors.New("lines count mismatch, should be 3") } mostListened = MostListenedSong{} if mostListened.Date, err = time.Parse(time.RFC3339, string(lines[0])); err != nil { return err } if mostListened.Listeners, err = strconv.Atoi(string(lines[1])); err != nil { return err } mostListened.Song = string(lines[2]) return nil } func StoreMostListenedSong() []byte { if !mlsChanged { return nil } buf := make([]byte, 0, 30+len(mostListened.Song)) b := bytes.NewBuffer(buf) b.WriteString(mostListened.Date.Format(time.RFC3339)) b.WriteByte('\n') b.WriteString(strconv.Itoa(mostListened.Listeners)) b.WriteByte('\n') b.WriteString(mostListened.Song) return b.Bytes() }