Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

json.gno

4.53 Kb · 127 lines
  1package radio
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/nym-alexiscolin000/gnoradio/text/v0"
  8	"gno.land/r/nym-alexiscolin000/gnoradio/catalog/v1"
  9)
 10
 11// Read API for the GnoRadio app (vm/qeval).
 12
 13// ScheduleJSON returns what a station plays from now until now+horizon seconds
 14// (60..7200, at most 60 entries): programmed slots and the rotation between
 15// them (on Main, the hour's genre station, simulcast and cut at each hour). A client plays entry i from offset+max(0, clock-start) and resyncs on
 16// "now". "relay" marks, on Main, an entry that is not Main's own slot but
 17// the hour's genre station simulcast (its picks included). "sponsored" is the refund its artist pays the picker (0: a normal
 18// pick), "at" the time a booked pick asked for (0: none). "booked" lists every
 19// booked pick still to air, whatever the horizon, and those that aired in the
 20// last hour (they count toward their clock hour's cap until they fold):
 21// {"track","start","end","at","by","note"}. Fetch it again every few minutes, not
 22// every second.
 23func ScheduleJSON(stationID, horizon int) string {
 24	st := rd().station(stationID)
 25	if horizon < 60 || horizon > 7200 {
 26		horizon = 3600
 27	}
 28	ts := now()
 29	end := ts + int64(horizon)
 30	var b strings.Builder
 31	b.WriteString(`{"station":` + text.Itoa(st.id) + `,"now":` + text.Itoa64(ts) + `,"entries":[`)
 32	n := 0
 33	emit := func(track int, title string, start, stop, offset int64, queued bool, by, note string, pay, at int64, relay bool) {
 34		if n > 0 {
 35			b.WriteString(",")
 36		}
 37		b.WriteString(`{"track":` + text.Itoa(track) + `,"title":` + text.Str(title) +
 38			`,"start":` + text.Itoa64(start) + `,"end":` + text.Itoa64(stop) + `,"offset":` + text.Itoa64(offset) +
 39			`,"queued":` + strconv.FormatBool(queued) + `,"by":` + text.Str(by) + `,"note":` + text.Str(note) + `,"sponsored":` + text.Itoa64(pay) + `,"at":` + text.Itoa64(at) + `,"relay":` + strconv.FormatBool(relay) + `}`)
 40		n++
 41	}
 42	cur := ts
 43	for cur < end && n < 60 {
 44		var slot *Slot
 45		next := int64(-1)
 46		sl := st.slots()
 47		for i := range sl {
 48			s := &sl[i]
 49			if cur >= s.Start && cur < s.Start+s.Dur {
 50				slot = s
 51				break
 52			}
 53			if s.Start > cur && (next < 0 || s.Start < next) {
 54				next = s.Start
 55			}
 56		}
 57		if slot != nil {
 58			stop := slot.Start + slot.Dur
 59			if title := catalog.PlayableTitle(slot.Track); title != "" {
 60				emit(slot.Track, title, slot.Start, stop, 0, true, slot.By.String(), slot.shownNote(), slot.Pay, slot.At, false)
 61			}
 62			cur = stop
 63			continue
 64		}
 65		track, off, dur, ok := st.between(cur)
 66		relay := st.id == MainStation // not Main's own slot: the hour's genre station, simulcast
 67		if !ok {
 68			if next < 0 {
 69				break
 70			}
 71			cur = next
 72			continue
 73		}
 74		stop := cur + dur - off
 75		if next >= 0 && next < stop {
 76			stop = next // the rotation (or Main's relay) pauses for the programmed slot
 77		}
 78		if title := catalog.PlayableTitle(track); title != "" {
 79			if s, ok := st.pickAt(cur); ok && s.Track == track { // Main simulcasting a genre station's pick
 80				emit(track, title, cur-off, stop, off, true, s.By.String(), s.shownNote(), s.Pay, s.At, relay)
 81			} else {
 82				emit(track, title, cur-off, stop, off, false, "", "", 0, 0, relay)
 83			}
 84		}
 85		cur = stop
 86	}
 87	b.WriteString(`],"booked":[`)
 88	n = 0
 89	for _, s := range st.slots() {
 90		if s.At > 0 && s.Start+s.Dur > ts-foldAfter {
 91			if n > 0 {
 92				b.WriteString(",")
 93			}
 94			b.WriteString(`{"track":` + text.Itoa(s.Track) + `,"start":` + text.Itoa64(s.Start) + `,"end":` + text.Itoa64(s.Start+s.Dur) + `,"at":` + text.Itoa64(s.At) + `,"by":` + text.Str(s.By.String()) + `,"note":` + text.Str(s.shownNote()) + `}`)
 95			n++
 96		}
 97	}
 98	b.WriteString(`]}`)
 99	return b.String()
100}
101
102// StationsJSON lists every station with what it plays now.
103func StationsJSON() string {
104	t := rd()
105	var b strings.Builder
106	// newFloor: only tracks above this id may be picked on New this week.
107	synced := t.synced()
108	floor := synced - newCap
109	if floor < 0 {
110		floor = 0
111	}
112	ts := now()
113	b.WriteString(`{"pending":` + text.Itoa(catalog.TrackCount()-synced) + `,"newFloor":` + text.Itoa(floor) + `,"stations":[`)
114	for id := 0; id < numStations; id++ {
115		st := t.station(id)
116		if id > 0 {
117			b.WriteString(",")
118		}
119		track, offset, isQueued := st.onAir(ts)
120		live, loop := st.rotating()
121		b.WriteString(`{"id":` + text.Itoa(id) + `,"name":` + text.Str(StationName(id)) + `,"genre":` + text.Itoa(st.genre) +
122			`,"tracks":` + text.Itoa(live) + `,"loop":` + text.Itoa64(loop) + `,"queued":` + text.Itoa(st.queued(ts)) +
123			`,"now":{"track":` + text.Itoa(track) + `,"offset":` + text.Itoa64(offset) + `,"queued":` + strconv.FormatBool(isQueued) + `}}`)
124	}
125	b.WriteString(`]}`)
126	return b.String()
127}