read.gno
9.17 Kb · 275 lines
1package catalog
2
3import (
4 "strings"
5
6 "gno.land/p/nym-alexiscolin000/gnoradio/store/v0"
7 "gno.land/p/nym-alexiscolin000/gnoradio/text/v0"
8 "gno.land/r/nym-alexiscolin000/gnoradio/data"
9)
10
11// ---- Read API for the other GnoRadio realms (values only) ----
12
13func GenreName(g int) string {
14 if g < 1 || g > NumGenres {
15 return ""
16 }
17 return genres[g]
18}
19
20func TrackCount() int { return int(rd().count(mTracks)) }
21func ArtistCount() int { return int(rd().count(mArtists)) }
22func AlbumCount() int { return int(rd().count(mAlbums)) }
23func PlaylistCount() int { return int(rd().count(mPlaylists)) }
24
25// TrackBrief is what the radio needs; playable is false for an unknown or
26// hidden track, or one whose artist is hidden.
27func TrackBrief(id int) (artistID, genre int, duration int64, playable_ bool) {
28 rec := point(cTracks, id)
29 if rec == "" {
30 return 0, 0, 0, false
31 }
32 a := text.Atoi(store.Field(rec, fArtist))
33 arec := point(cArtists, a)
34 return a, text.Atoi(store.Field(rec, fGenre)), text.Atoi64(store.Field(rec, fDuration)),
35 store.Field(rec, fHidden) == "" && arec != "" && store.Field(arec, aHidden) == ""
36}
37
38// TrackBriefFor is TrackBrief and whether addr ("" for nobody) is the
39// track's artist or one of its collaborators, from the same reads.
40func TrackBriefFor(id int, addr address) (artistID, genre int, duration int64, playable_, own bool) {
41 rec := point(cTracks, id)
42 if rec == "" {
43 return 0, 0, 0, false, false
44 }
45 a := text.Atoi(store.Field(rec, fArtist))
46 arec := point(cArtists, a)
47 playable_ = store.Field(rec, fHidden) == "" && arec != "" && store.Field(arec, aHidden) == ""
48 if addr != "" && arec != "" {
49 // splits are "addr:pct,…" and an address holds no ',' or ':'
50 sp := store.Field(rec, fSplits)
51 own = store.Field(arec, aOwner) == addr.String() || sp != "" && strings.Contains(","+sp, ","+addr.String()+":")
52 }
53 return a, text.Atoi(store.Field(rec, fGenre)), text.Atoi64(store.Field(rec, fDuration)), playable_, own
54}
55
56// PlayableTitle is a track's title, "" unless it is playable (TrackBrief). An
57// Audius or Jamendo pointer stores no title: it is "Audius track" here, the
58// app names it from the platform.
59func PlayableTitle(id int) string {
60 rec := point(cTracks, id)
61 if rec == "" || store.Field(rec, fHidden) != "" {
62 return ""
63 }
64 if arec := point(cArtists, text.Atoi(store.Field(rec, fArtist))); arec == "" || store.Field(arec, aHidden) != "" {
65 return ""
66 }
67 return refLabel(store.Field(rec, fOrigin), store.Field(rec, fTitle), "track")
68}
69
70func TrackTitle(id int) string {
71 if rec := point(cTracks, id); rec != "" {
72 return refLabel(store.Field(rec, fOrigin), store.Field(rec, fTitle), "track")
73 }
74 return ""
75}
76
77func ArtistName(artistID int) string {
78 if rec := point(cArtists, artistID); rec != "" {
79 return refLabel(store.Field(rec, aKind), store.Field(rec, aName), "artist")
80 }
81 return ""
82}
83
84func ArtistOwner(artistID int) address { return address(artistAt(artistID, aOwner)) }
85func ArtistOf(owner address) int { return artistOf(rd(), owner) }
86
87// ArtistPayee is what a payment to an artist checks, from one read: who
88// is paid, whether verified, and whether the profile is visible.
89func ArtistPayee(id int) (owner address, verified, visible bool) {
90 rec := point(cArtists, id)
91 if rec == "" {
92 return "", false, false
93 }
94 return address(store.Field(rec, aOwner)), store.Field(rec, aVerified) == "1", store.Field(rec, aHidden) == ""
95}
96func ArtistVisible(artistID int) bool {
97 rec := point(cArtists, artistID)
98 return rec != "" && store.Field(rec, aHidden) == ""
99}
100
101// ---- Value getters for the home realm and tools ----
102
103// GetTrack returns a copy of a track; ok is false if unknown. A pointer's
104// empty title reads as its platform ("Audius track").
105func GetTrack(id int) (Track, bool) {
106 if tr := getTrack(rd(), id); tr != nil {
107 tr.Title = refLabel(tr.Origin, tr.Title, "track")
108 return *tr, true
109 }
110 return Track{}, false
111}
112
113func GetArtist(id int) (Artist, bool) {
114 t := rd()
115 if a := getArtist(t, id); a != nil {
116 a.Name = refLabel(a.Kind, a.Name, "artist")
117 return *withLists(t, a), true
118 }
119 return Artist{}, false
120}
121
122// ArtistByRef looks a pointer artist up by its platform id ("audius-user:12").
123func ArtistByRef(ref string) int {
124 v, _ := data.Get(cNames, strings.TrimSpace(ref))
125 return text.Atoi(v)
126}
127
128// ArtistByName looks a profile up by its (case-insensitive) name.
129func ArtistByName(name string) int {
130 v, _ := data.Get(cNames, skeleton(strings.TrimSpace(name)))
131 return text.Atoi(v)
132}
133
134func GetAlbum(id int) (Album, bool) {
135 if a := getAlbum(rd(), id); a != nil {
136 return *a, true
137 }
138 return Album{}, false
139}
140
141func GetPlaylist(id int) (Playlist, bool) {
142 if p := getPlaylist(rd(), id); p != nil {
143 return *p, true
144 }
145 return Playlist{}, false
146}
147
148// ReportCount is the number of open (unresolved) reports.
149func ReportCount() int { return int(rd().count(mOpen)) }
150
151func GetReport(id int) (ReportInfo, bool) { return getReport(rd(), id) }
152
153// UserStats returns a listener's public counters and playlists. The like
154// count reads the listener's own like ranges, one page.
155func UserStats(addr address) (likes, follows int, tipped int64, lists []int) {
156 _, likes = rangePage(cLikes, addr.String(), 0, 0)
157 u := user(rd(), addr)
158 return likes, store.Count(u.Follows), u.Tipped, u.Playlists
159}
160
161// LikedPage lists a listener's liked track ids, newest tracks first,
162// paginated. Likes are keyed by track id, so this is the newest music the
163// listener liked, not the order of their likes (that needs an index per like,
164// which would add to every like's storage deposit).
165func LikedPage(addr address, offset, limit int) []int {
166 ids, _ := rangePage(cLikes, addr.String(), max0(offset), store.Limit(limit, 100))
167 return ids
168}
169
170// ---- Narrow getters: one field without copying a whole struct ----
171
172// ArtistTrackCount is the number of tracks an artist published.
173func ArtistTrackCount(artistID int) int {
174 tl, _ := lists(rd(), artistID)
175 return store.Count(tl)
176}
177
178// ArtistTrackPage lists an artist's track ids in publishing order, paginated.
179func ArtistTrackPage(artistID, offset, limit int) []int {
180 if offset < 0 {
181 return nil
182 }
183 tl, _ := lists(rd(), artistID)
184 return store.Page(tl, offset, store.Limit(limit, 100), false)
185}
186
187// ArtistBrief returns what a link to an artist needs, without the bio or the
188// id lists; ok is false for an unknown artist.
189func ArtistBrief(artistID int) (name string, verified, hidden, ok bool) {
190 rec := point(cArtists, artistID)
191 if rec == "" {
192 return "", false, false, false
193 }
194 return refLabel(store.Field(rec, aKind), store.Field(rec, aName), "artist"), store.Field(rec, aVerified) != "", store.Field(rec, aHidden) != "", true
195}
196
197// PlaylistBrief returns a playlist's header without its track ids.
198func PlaylistBrief(id int) (owner address, title string, tracks int, hidden, ok bool) {
199 rec := rd().val(cPlaylists, store.Pad(id)) // counts the ids without decoding them
200 if id < 1 || rec == "" {
201 return "", "", 0, false, false
202 }
203 f := store.Fields(rec)
204 return address(f[0]), f[1], store.Count(f[4]), f[3] != "", true
205}
206
207// FollowsPage lists the artist ids a listener follows, paginated.
208func FollowsPage(addr address, offset, limit int) []int {
209 return store.Page(user(rd(), addr).Follows, max0(offset), store.Limit(limit, 100), false)
210}
211
212// GenrePage lists the track ids of a genre (hidden ones included), newest first.
213func GenrePage(genre, offset, limit int) []int {
214 if genre < 1 || genre > NumGenres {
215 return nil
216 }
217 ids, _ := rangePage(cGenres, store.Pad(genre), max0(offset), store.Limit(limit, 100))
218 return ids
219}
220
221// GenreCount is the number of tracks filed under a genre.
222func GenreCount(genre int) int {
223 if genre < 1 || genre > NumGenres {
224 return 0
225 }
226 return int(rd().count(mGenre + genre))
227}
228
229// TrackPage lists track ids, newest first when newest is true.
230// Ids are dense (1..count), so these pages are arithmetic.
231func TrackPage(offset, limit int, newest bool) []int {
232 return seqPage(TrackCount(), offset, limit, newest)
233}
234func ArtistPage(offset, limit int) []int { return seqPage(ArtistCount(), offset, limit, false) }
235func AlbumPage(offset, limit int) []int { return seqPage(AlbumCount(), offset, limit, true) }
236func PlaylistPage(offset, limit int) []int { return seqPage(PlaylistCount(), offset, limit, true) }
237
238// ReportPage lists open report ids, newest first.
239func ReportPage(offset, limit int) []int {
240 keys, _ := store.Rows(data.PageAt(cReports, max0(offset), store.Limit(limit, 100), true))
241 out := make([]int, 0, len(keys))
242 for _, k := range keys {
243 out = append(out, text.Atoi(k))
244 }
245 return out
246}
247
248func seqPage(n, offset, limit int, newest bool) []int {
249 var out []int
250 for k := max0(offset); k < n && len(out) < store.Limit(limit, 100); k++ {
251 if newest {
252 out = append(out, n-k)
253 } else {
254 out = append(out, k+1)
255 }
256 }
257 return out
258}
259
260func max0(n int) int {
261 if n < 0 {
262 return 0
263 }
264 return n
265}
266
267func TopLiked() []Rank { return rd().top(topLiked) }
268func TopArtists() []Rank { return rd().top(topArtists) }
269func TopFans() []Rank { return rd().top(topFans) }
270
271func Admin() address { return adminAddr() }
272func PendingAdmin() address { return admin.Pending() } // the address the admin role is offered to, "" if none
273func Version() string { return version }
274func RightsTerms() string { return rightsTerms }
275func HostAllowed(h string) bool { return data.Has(cHosts, strings.ToLower(h)) }