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