1
0
dwelling-radio/internal/http/dj_handlers.go

138 lines
3.7 KiB
Go
Raw Normal View History

package http
import (
"dwelling-radio/internal/radio"
2024-05-10 00:07:07 +04:00
"dwelling-radio/internal/statistics"
2023-10-01 05:43:52 +04:00
"dwelling-radio/pkg/oggtag"
2023-10-01 03:35:33 +04:00
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
2023-10-01 05:43:52 +04:00
"time"
)
type DJHandlers struct {
listeners *radio.ListenerCounter
playlist *radio.Playlist
2024-05-10 00:07:07 +04:00
stats statistics.Statistics
curSong *radio.Song
listLen int64
fallbackSong string
}
func NewDJHandlers(l *radio.ListenerCounter, p *radio.Playlist,
2024-05-10 00:07:07 +04:00
stats statistics.Statistics, cs *radio.Song, n int64, fS string) *DJHandlers {
return &DJHandlers{listeners: l, playlist: p,
stats: stats, curSong: cs, listLen: n, fallbackSong: fS}
}
func (dj *DJHandlers) ListenersUpdate(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Println("DJHandlers.ListenersUpdate panic:", err)
}
}()
if err := r.ParseForm(); err != nil {
log.Println("DJHandlers.ListenersUpdate:", err)
http.Error(w, "cannot parse form", http.StatusBadRequest)
return
}
switch r.FormValue("action") {
case "listener_add":
l := dj.listeners.Inc()
2024-05-10 00:07:07 +04:00
go func() {
dj.curSong.UpdateMaxListeners(l)
dj.curSong.IncListeners()
}()
case "listener_remove":
if _, err := dj.listeners.Dec(); err != nil {
log.Println("DJHandlers.ListenersUpdate:", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
default:
w.WriteHeader(http.StatusNotAcceptable)
return
}
w.Header().Add("Content-Type", "text/plain")
w.Header().Add("Icecast-Auth-User", "1")
w.WriteHeader(http.StatusOK)
}
2023-10-01 01:32:57 +04:00
func (dj *DJHandlers) PlaylistNext(w http.ResponseWriter, _ *http.Request) {
w.Header().Add("Content-Type", "text/plain")
nxt := dj.playlist.Next()
if nxt == "" {
log.Println("the end of a playlist has been reached")
if nxt = dj.fallbackSong; nxt == "" {
log.Println("a fallback song is not set")
http.Error(w, "a playlist is empty and a fallback song is not set", http.StatusNotFound)
return
}
}
2023-10-01 05:43:52 +04:00
go func() {
oggf, err := oggtag.NewOggFile(nxt)
if err != nil {
log.Println("cannot read an OGG file", nxt, ":", err)
http.Error(w, "cannot read an OGG file", http.StatusInternalServerError)
return
}
2023-10-08 00:52:40 +04:00
2024-05-10 00:07:07 +04:00
newSong := radio.Song{
Artist: oggf.GetTag("artist"),
Title: oggf.GetTag("title"),
Duration: oggf.GetDuration(),
2024-05-10 00:07:07 +04:00
Listeners: dj.listeners.Current(),
MaxListeners: dj.listeners.Current(),
// Here 5 seconds are being added because it is approximately the
// time between the creation of this Song object and when ezstream
// actually starts to play it.
StartAt: time.Now().Add(5 * time.Second)}
2023-10-08 00:52:40 +04:00
2024-05-10 00:07:07 +04:00
if newSong.Artist == "" || newSong.Title == "" {
log.Println("Playlist:", nxt, "has no artist or title tags.")
}
if strings.HasSuffix(nxt, "/fallback.ogg") {
2024-05-10 00:07:07 +04:00
newSong.Artist = "Nothing to play. Playing a fallback: " + newSong.Artist
}
2024-05-10 00:07:07 +04:00
if dj.curSong.Artist != "" {
if err := dj.stats.Add(dj.curSong); err != nil {
log.Println("cannot add a song to a stats DB:", err)
}
}
2024-05-10 00:07:07 +04:00
dj.curSong.SetFrom(&newSong)
}()
2024-05-10 00:07:07 +04:00
fmt.Fprintln(w, nxt)
2023-10-01 01:32:57 +04:00
}
2023-10-01 03:35:33 +04:00
func (dj *DJHandlers) Status(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
2024-05-10 00:07:07 +04:00
lst, err := dj.stats.LastNSongs(dj.listLen)
if err != nil {
log.Println("failed to fetch last n songs:", err)
}
err = json.NewEncoder(w).Encode(&struct {
Current *radio.Song `json:"current_song,omitempty"`
Listeners *radio.ListenerCounter `json:"listeners"`
List []radio.Song `json:"last_songs,omitempty"`
}{
2024-05-10 00:07:07 +04:00
Current: dj.curSong,
Listeners: dj.listeners,
2024-05-10 00:07:07 +04:00
List: lst})
if err != nil {
log.Println("DJHandlers.Status:", err)
http.Error(w, "status parsing failed", http.StatusInternalServerError)
}
}