Skip to content

Commit

Permalink
website: add faves pagination and json downloading
Browse files Browse the repository at this point in the history
  • Loading branch information
Wessie committed Feb 22, 2024
1 parent 6758e59 commit ae8addc
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 19 deletions.
3 changes: 2 additions & 1 deletion ircbot/commands_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ func RandomTrackRequest(e Event) error {
nickname = nick
}

songs, err = e.Storage.Song(e.Ctx).FavoritesOf(nickname)
// TODO(wessie): this limit is artificial, but no one should hit 100k faves
songs, err = e.Storage.Song(e.Ctx).FavoritesOf(nickname, 100000, 0)
if err != nil {
return errors.E(op, err)
}
Expand Down
2 changes: 1 addition & 1 deletion radio.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ type SongStorage interface {
// Favorites returns all users that have this song on their favorite list
Favorites(Song) ([]string, error)
// FavoritesOf returns all songs that are on a users favorite list
FavoritesOf(nick string) ([]Song, error)
FavoritesOf(nick string, limit, offset int64) ([]Song, error)
// AddFavorite adds the given song to nicks favorite list
AddFavorite(song Song, nick string) (bool, error)
// RemoveFavorite removes the given song from nicks favorite list
Expand Down
8 changes: 5 additions & 3 deletions storage/mariadb/track.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,16 +318,18 @@ JOIN
WHERE
tracks.usable = 1
AND
enick.nick = ?;
enick.nick = ?
ORDER BY esong.meta ASC
LIMIT ? OFFSET ?;
`)

// FavoritesOf implements radio.SongStorage
func (ss SongStorage) FavoritesOf(nick string) ([]radio.Song, error) {
func (ss SongStorage) FavoritesOf(nick string, limit, offset int64) ([]radio.Song, error) {
const op errors.Op = "mariadb/SongStorage.FavoritesOf"

var songs = []radio.Song{}

err := sqlx.Select(ss.handle, &songs, songFavoritesOfQuery, nick)
err := sqlx.Select(ss.handle, &songs, songFavoritesOfQuery, nick, limit, offset)
if err != nil {
return nil, errors.E(op, err)
}
Expand Down
5 changes: 0 additions & 5 deletions streamer/streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ var (
bufferMP3Size = 1024 * 32 // about 1.3 seconds of audio
bufferPCMSize = 1024 * 64 // about 0.4 seconds of audio
)
var (
httpOK = []byte("HTTP/1.0 200 OK")
httpMountInUse = []byte("HTTP/1.0 403 Mountpoint in use")
httpUnauthorized = []byte("HTTP/1.0 401 Unauthorized")
)

// Streamer represents a single icecast stream
type Streamer struct {
Expand Down
2 changes: 1 addition & 1 deletion website/middleware/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func UserByDJIDCtx(storage radio.UserStorageService) func(http.Handler) http.Han
tmp1 := chi.URLParamFromCtx(ctx, "DJID")
tmp2, err := strconv.Atoi(tmp1)
if err != nil {
http.Error(w, http.StatusText(404), 404)
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
id := radio.DJID(tmp2)
Expand Down
83 changes: 75 additions & 8 deletions website/public/faves.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package public

import (
"encoding/json"
"fmt"
"html"
"net/http"

radio "github.com/R-a-dio/valkyrie"
"github.com/R-a-dio/valkyrie/website/middleware"
"github.com/R-a-dio/valkyrie/website/shared"
"github.com/go-chi/chi/v5"
)

const favesPageSize = 100

type FavesInput struct {
middleware.Input
Nickname string
Faves []radio.Song
Page *shared.Pagination
Nickname string
DownloadURL string
Faves []radio.Song
Page *shared.Pagination
}

func (FavesInput) TemplateBundle() string {
Expand All @@ -26,17 +31,29 @@ func NewFavesInput(ss radio.SongStorage, r *http.Request) (*FavesInput, error) {
if err != nil {
return nil, err
}
_ = offset

nickname := r.FormValue("nick")
faves, err := ss.FavoritesOf(nickname)
// we support both ?nick=<nick> and /faves/<nick> so we need to see which one we have
// try the old format first, and then the GET parameter
nickname := chi.URLParam(r, "Nick")
if nickname == "" {
nickname = r.FormValue("nick")
}
nickname = html.EscapeString(nickname)

faves, err := ss.FavoritesOf(nickname, favesPageSize, offset)
if err != nil {
return nil, err
}

q := r.URL.Query()
q.Set("dl", "true")
dlUrl := *r.URL
dlUrl.RawQuery = q.Encode()

return &FavesInput{
Nickname: nickname,
Faves: faves,
Nickname: nickname,
DownloadURL: dlUrl.String(),
Faves: faves,
Page: shared.NewPagination(
page, shared.PageCount(int64(len(faves)), favesPageSize),
r.URL,
Expand All @@ -52,6 +69,18 @@ func (s State) GetFaves(w http.ResponseWriter, r *http.Request) {
return
}

// we have an old API that returns your faves as JSON if you use dl=true
// so we need to support that for old users
if r.FormValue("dl") != "" {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s_faves.json", input.Nickname))
err := json.NewEncoder(w).Encode(NewFaveDownload(input.Faves))
if err != nil {
s.errorHandler(w, r, err)
}
return
}

err = s.Templates.Execute(w, r, input)
if err != nil {
s.errorHandler(w, r, err)
Expand All @@ -61,3 +90,41 @@ func (s State) GetFaves(w http.ResponseWriter, r *http.Request) {

func (s State) PostFaves(w http.ResponseWriter, r *http.Request) {
}

type FaveDownloadEntry struct {
ID *radio.TrackID `json:"tracks_id"`
Metadata string `json:"meta"`
LastRequested *int64 `json:"lastrequested"`
LastPlayed *int64 `json:"lastplayed"`
RequestCount *int `json:"requestcount"`
}

func NewFaveDownloadEntry(song radio.Song) FaveDownloadEntry {
var entry FaveDownloadEntry

if song.HasTrack() {
if song.TrackID > 0 {
entry.ID = &song.TrackID
}
if !song.LastRequested.IsZero() {
tmp := song.LastRequested.Unix()
entry.LastRequested = &tmp
}
entry.RequestCount = &song.RequestCount
}

if !song.LastPlayed.IsZero() {
tmp := song.LastPlayed.Unix()
entry.LastPlayed = &tmp
}

return entry
}

func NewFaveDownload(songs []radio.Song) []FaveDownloadEntry {
res := make([]FaveDownloadEntry, 0, len(songs))
for _, song := range songs {
res = append(res, NewFaveDownloadEntry(song))
}
return res
}
1 change: 1 addition & 0 deletions website/public/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func Route(ctx context.Context, s State) func(chi.Router) {
r.Post("/submit", s.PostSubmit)
r.Get("/staff", s.GetStaff)
r.Get("/faves", s.GetFaves)
r.Get("/faves/{Nick}", s.GetFaves)
r.Post("/faves", s.PostFaves)
r.Get("/irc", s.GetChat)
}
Expand Down

0 comments on commit ae8addc

Please sign in to comment.