1
0
dwelling-radio/internal/radio/icecast.go

165 lines
3.8 KiB
Go
Raw Normal View History

2022-03-08 01:17:24 +04:00
package radio
import (
"dwelling-radio/pkg/watcher"
2022-03-08 01:17:24 +04:00
"encoding/json"
"fmt"
"net/http"
"os/exec"
"strings"
"time"
"github.com/pkg/errors"
2022-03-08 01:17:24 +04:00
)
type IcecastStatusDTO struct {
Icestats struct {
ServerStartISO8601 string `json:"server_start_iso8601"`
ServerStartDate string `json:"server_start"`
Source struct {
Artist string `json:"artist"`
Title string `json:"title"`
ListenerPeak int `json:"listener_peak"`
Listeners int `json:"listeners"`
} `json:"source"`
} `json:"icestats"`
}
func (is *IcecastStatusDTO) Song() string {
return is.Icestats.Source.Artist + " - " + is.Icestats.Source.Title
}
2022-03-08 01:17:24 +04:00
type IcecastStatus struct {
ServerStartISO8601 string `json:"server_start_iso8601"`
2022-03-31 02:16:15 +04:00
ServerStartDate string `json:"server_start_date"`
2022-03-08 01:17:24 +04:00
SongName string `json:"song"`
ListenerPeak int `json:"listener_peak"`
Listeners int `json:"listeners"`
}
type Song struct {
Time string `json:"time"`
Artist string `json:"artist"`
Title string `json:"title"`
}
func IcecastGetStatus(icecastURL string) (*IcecastStatus, error) {
resp, err := http.Get(icecastURL)
if err != nil {
return nil, err
}
iceStatDTO := &IcecastStatusDTO{}
if err := json.NewDecoder(resp.Body).Decode(iceStatDTO); err != nil {
return nil, err
}
return &IcecastStatus{
2022-03-08 01:17:24 +04:00
ServerStartISO8601: iceStatDTO.Icestats.ServerStartISO8601,
ServerStartDate: iceStatDTO.Icestats.ServerStartDate,
SongName: iceStatDTO.Song(),
ListenerPeak: iceStatDTO.Icestats.Source.ListenerPeak,
Listeners: iceStatDTO.Icestats.Source.Listeners,
}, nil
2022-03-08 01:17:24 +04:00
}
func IcecastLastPlayedSongs(lastNSongs int, playlistPath string) ([]Song, error) {
if lpcLen := len(lastPlayedCache); lpcLen > 0 {
if lastNSongs > lpcLen {
lastNSongs = lpcLen
}
return lastPlayedCache[lpcLen-lastNSongs:], nil
}
return lastPlayedSongs(lastNSongs, playlistPath)
}
func IcecastLastSong(playlistPath string) (Song, error) {
if lpcLen := len(lastPlayedCache); lpcLen > 0 {
return lastPlayedCache[lpcLen-1], nil
}
songs, err := lastPlayedSongs(1, playlistPath)
if len(songs) == 0 {
return Song{}, nil
}
return songs[0], err
}
func lastPlayedSongs(lastNSongs int, playlistPath string) ([]Song, error) {
2022-03-08 01:17:24 +04:00
songs := make([]Song, 0)
cmd := fmt.Sprintf("tail -n%d %s | head -n-1 | cut -d'|' -f1,4", lastNSongs+1, playlistPath)
out, err := exec.Command("bash", "-c", cmd).CombinedOutput()
if err != nil {
return songs, err
}
2022-03-08 01:17:24 +04:00
if len(out) == 0 {
return songs, nil
2022-03-08 01:17:24 +04:00
}
songs_ := strings.Split(string(out), "\n")
for _, song := range songs_ {
ts := strings.Split(song, "|")
if len(ts) <= 1 {
continue
}
2022-03-31 02:16:34 +04:00
tim, _ := time.Parse("02/Jan/2006:15:04:05 -0700", ts[0])
at := strings.Split(ts[1], " - ")
2022-03-08 01:17:24 +04:00
songs = append(songs, Song{
2022-03-31 17:58:42 +04:00
Time: tim.Format("15:04-0700"),
2022-03-08 01:17:24 +04:00
Artist: at[0],
Title: at[1]})
}
return songs, nil
2022-03-08 01:17:24 +04:00
}
var playlistWatcher watcher.InotifyWatcher
var playlistFired chan uint32
var lastPlayedCache []Song
func IcecastWatchPlaylist(playlistPath string, lastNSongs int) error {
playlistWatcher, err := watcher.NewInotifyWatcher()
if err != nil {
return errors.Wrap(err, "cannot instantiate inotify watcher")
}
err = playlistWatcher.AddWatch(playlistPath, watcher.ModMask)
if err != nil {
return errors.Wrap(err, "cannot set a playlist to watch")
}
playlistFired = make(chan uint32)
playlistWatcher.WatchForMask(playlistFired, watcher.ModMask)
go func() {
for {
select {
case <-playlistFired:
songs, err := lastPlayedSongs(lastNSongs, playlistPath)
if err == nil && len(songs) > 0 {
lastPlayedCache = songs
}
}
}
}()
songs, err := lastPlayedSongs(lastNSongs, playlistPath)
if err == nil && len(songs) > 0 {
lastPlayedCache = songs
}
return nil
}
func IcecastWatchClose() {
playlistWatcher.Close()
}