2023-09-30 23:59:48 +04:00
|
|
|
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"
|
2023-09-30 23:59:48 +04:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2023-10-08 02:56:19 +04:00
|
|
|
"strings"
|
2023-10-01 05:43:52 +04:00
|
|
|
"time"
|
2023-09-30 23:59:48 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
type DJHandlers struct {
|
2023-10-08 02:52:37 +04:00
|
|
|
listeners *radio.ListenerCounter
|
|
|
|
playlist *radio.Playlist
|
2024-05-10 00:07:07 +04:00
|
|
|
stats statistics.Statistics
|
|
|
|
curSong *radio.Song
|
|
|
|
listLen int64
|
2023-10-08 02:52:37 +04:00
|
|
|
fallbackSong string
|
2023-09-30 23:59:48 +04:00
|
|
|
}
|
|
|
|
|
2023-10-08 02:52:37 +04:00
|
|
|
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}
|
2023-09-30 23:59:48 +04:00
|
|
|
}
|
|
|
|
|
2024-05-16 01:59:26 +04:00
|
|
|
func (dj *DJHandlers) ListenersUpdateIcecast(w http.ResponseWriter, r *http.Request) {
|
2023-10-08 22:17:56 +04:00
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
2024-05-16 01:59:26 +04:00
|
|
|
log.Println("DJHandlers.ListenersUpdateIcecast panic:", err)
|
2023-10-08 22:17:56 +04:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
2024-05-16 01:59:26 +04:00
|
|
|
log.Println("DJHandlers.ListenersUpdateIcecast:", err)
|
2024-05-22 03:40:33 +04:00
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2023-10-08 22:17:56 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch r.FormValue("action") {
|
|
|
|
case "listener_add":
|
2024-05-22 04:05:15 +04:00
|
|
|
dj.listeners.Lock()
|
2024-05-11 04:36:23 +04:00
|
|
|
_ = dj.listeners.Inc()
|
2024-05-22 04:05:15 +04:00
|
|
|
dj.listeners.Unlock()
|
2023-10-08 22:17:56 +04:00
|
|
|
case "listener_remove":
|
2024-05-22 04:05:15 +04:00
|
|
|
dj.listeners.Lock()
|
|
|
|
defer dj.listeners.Unlock()
|
2023-10-08 22:17:56 +04:00
|
|
|
if _, err := dj.listeners.Dec(); err != nil {
|
2024-05-16 01:59:26 +04:00
|
|
|
log.Println("DJHandlers.ListenersUpdateIcecast:", err)
|
2024-05-22 03:40:33 +04:00
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2023-10-08 22:17:56 +04:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2024-05-11 04:36:23 +04:00
|
|
|
const defaultArtistTag = "[LOL, no artist tag]"
|
|
|
|
const defaultTitleTag = "[No title tag for you -_-]"
|
|
|
|
const defaultTitleTagNoArtist = "[And no title tag either! Pffft]"
|
|
|
|
|
2023-10-01 01:32:57 +04:00
|
|
|
func (dj *DJHandlers) PlaylistNext(w http.ResponseWriter, _ *http.Request) {
|
|
|
|
w.Header().Add("Content-Type", "text/plain")
|
2024-05-22 04:07:33 +04:00
|
|
|
dj.playlist.Lock()
|
2023-10-01 01:32:57 +04:00
|
|
|
nxt := dj.playlist.Next()
|
2024-05-22 04:07:33 +04:00
|
|
|
dj.playlist.Unlock()
|
2023-10-01 01:32:57 +04:00
|
|
|
if nxt == "" {
|
|
|
|
log.Println("the end of a playlist has been reached")
|
2023-10-08 02:52:37 +04:00
|
|
|
if nxt = dj.fallbackSong; nxt == "" {
|
|
|
|
log.Println("a fallback song is not set")
|
2024-05-22 03:40:33 +04:00
|
|
|
w.WriteHeader(http.StatusNotFound)
|
2023-10-08 02:52:37 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2023-10-01 05:43:52 +04:00
|
|
|
|
2024-05-11 04:36:23 +04:00
|
|
|
oggf, err := oggtag.NewOggFile(nxt)
|
|
|
|
if err != nil {
|
|
|
|
log.Println("cannot read an OGG file", nxt, ":", err)
|
2024-05-22 03:40:33 +04:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2024-05-11 04:36:23 +04:00
|
|
|
return
|
|
|
|
}
|
2023-10-08 00:52:40 +04:00
|
|
|
|
2024-05-11 04:36:23 +04:00
|
|
|
newSong := radio.Song{
|
|
|
|
Artist: oggf.GetTag("artist"),
|
|
|
|
Title: oggf.GetTag("title"),
|
|
|
|
Duration: oggf.GetDuration(),
|
|
|
|
// 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.
|
2024-05-16 01:44:45 +04:00
|
|
|
StartAt: time.Now()} // .Add(5 * time.Second)
|
2024-05-11 04:36:23 +04:00
|
|
|
|
|
|
|
if newSong.Artist == "" && newSong.Title == "" {
|
|
|
|
log.Println("Playlist:", nxt, "has no artist and title tags.")
|
|
|
|
newSong.Artist = defaultArtistTag
|
|
|
|
newSong.Title = defaultTitleTagNoArtist
|
|
|
|
} else if newSong.Artist == "" {
|
|
|
|
log.Println("Playlist:", nxt, "has no artist tag.")
|
|
|
|
newSong.Artist = defaultArtistTag
|
|
|
|
} else if newSong.Title == "" {
|
|
|
|
log.Println("Playlist:", nxt, "has no title tag.")
|
|
|
|
newSong.Title = defaultTitleTag
|
|
|
|
}
|
2024-05-10 00:07:07 +04:00
|
|
|
|
2024-05-11 04:36:23 +04:00
|
|
|
if strings.HasSuffix(nxt, "/fallback.ogg") {
|
|
|
|
newSong.Artist = "Nothing to play. Playing a fallback: " + newSong.Artist
|
|
|
|
}
|
2023-10-08 02:56:19 +04:00
|
|
|
|
2024-05-22 04:05:15 +04:00
|
|
|
dj.listeners.Lock()
|
2024-05-11 04:52:40 +04:00
|
|
|
dj.curSong.Listeners, dj.curSong.PeakListeners = dj.listeners.Reset()
|
2024-05-22 04:05:15 +04:00
|
|
|
dj.listeners.Unlock()
|
2024-05-11 04:36:23 +04:00
|
|
|
|
2024-05-13 01:14:05 +04:00
|
|
|
if dj.curSong.Artist != "" && !strings.Contains(dj.curSong.Artist, "no artist tag") &&
|
|
|
|
!strings.Contains(dj.curSong.Artist, "No title tag") {
|
2024-05-11 04:36:23 +04:00
|
|
|
if err := dj.stats.Add(dj.curSong); err != nil {
|
|
|
|
log.Println("cannot add a song to a stats DB:", err)
|
2023-10-08 02:52:37 +04:00
|
|
|
}
|
2024-05-11 04:36:23 +04:00
|
|
|
}
|
2023-10-08 02:52:37 +04:00
|
|
|
|
2024-05-11 04:36:23 +04:00
|
|
|
*dj.curSong = newSong
|
|
|
|
dj.curSong.Listeners = 0
|
2024-05-11 04:52:40 +04:00
|
|
|
dj.curSong.PeakListeners = 0
|
2024-05-10 00:07:07 +04:00
|
|
|
|
2023-10-02 15:03:47 +04:00
|
|
|
fmt.Fprintln(w, nxt)
|
2023-10-01 01:32:57 +04:00
|
|
|
}
|
2023-10-01 03:35:33 +04:00
|
|
|
|
2023-10-01 22:07:04 +04:00
|
|
|
func (dj *DJHandlers) Status(w http.ResponseWriter, r *http.Request) {
|
2023-10-02 02:22:14 +04:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2024-05-22 04:05:15 +04:00
|
|
|
dj.listeners.RLock()
|
2024-06-18 19:50:56 +04:00
|
|
|
defer dj.listeners.RUnlock()
|
2024-05-10 00:07:07 +04:00
|
|
|
err = json.NewEncoder(w).Encode(&struct {
|
2024-05-12 03:57:54 +04:00
|
|
|
Current *radio.Song `json:"current_song,omitempty"`
|
|
|
|
Listeners int64 `json:"listeners"`
|
|
|
|
List []radio.Song `json:"last_songs,omitempty"`
|
2023-10-02 02:22:14 +04:00
|
|
|
}{
|
2024-05-10 00:07:07 +04:00
|
|
|
Current: dj.curSong,
|
2024-05-12 03:57:54 +04:00
|
|
|
Listeners: dj.listeners.Current(),
|
2024-05-10 00:07:07 +04:00
|
|
|
List: lst})
|
2023-10-02 02:22:14 +04:00
|
|
|
if err != nil {
|
|
|
|
log.Println("DJHandlers.Status:", err)
|
|
|
|
http.Error(w, "status parsing failed", http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
}
|