1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
/*
Package pvfm grabs information about PonyvilleFM from the station servers.
*/
package pvfm
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"time"
)
var (
latestInfo Wrapper
bugTime = flag.Int("pvfm-poke-delay", 15, "how stale the info can get")
)
// The regex to check for Aerial's name.
var (
AerialRegex = regexp.MustCompile(`Aerial`)
)
// Wrapper is a time, info pair. This is used to invalidate the cache of
// data from ponyvillefm.com.
type Wrapper struct {
Age time.Time
Info Info
}
// Info is the actual information we care about. It contains information about the
// available streams.
type Info struct {
Listeners Listeners `json:"all"`
Main RadioStream `json:"one"`
Secondary RadioStream `json:"two"`
MusicOnly RadioStream `json:"free"`
}
// RadioStream contains data about an individual stream.
type RadioStream struct {
Listeners int `json:"listeners"`
Nowplaying string `json:"nowplaying"`
Artist string `json:"artist"`
Album string `json:"album"`
Title string `json:"title"`
Onair string `json:"onair"`
Artwork string `json:"artwork"`
}
// Listeners contains a single variable Listeners.
type Listeners struct {
Listeners int `json:"listeners"`
}
// GetStats returns an Info, error pair representing the latest (or cached)
// version of the statistics from the ponyvillefm servers. If there is an error
// anywhere
func GetStats() (Info, error) {
now := time.Now()
// If right now is before the age of the latestInfo plus the pacing time,
// return the latestInfo Info.
if now.Before(latestInfo.Age.Add(time.Second * time.Duration(*bugTime))) {
return latestInfo.Info, nil
}
i := Info{}
// Grab stuff from the internet
c := &http.Client{
Timeout: time.Second * 15,
}
resp, err := c.Get("http://ponyvillefm.com/data/nowplaying")
if err != nil {
return Info{}, fmt.Errorf("http fetch: %s %d: %v", resp.Status, resp.StatusCode, err)
}
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
return Info{}, err
}
err = json.Unmarshal(content, &i)
if err != nil {
return Info{}, fmt.Errorf("json unmarshal: %v", err)
}
// Update the age/contents of the latestInfo
latestInfo.Info = i
latestInfo.Age = now
return latestInfo.Info, nil
}
// IsDJLive returns true if a human DJ is live or false if the auto DJ (and any
// of its playlists) is playing music.
func (i Info) IsDJLive() bool {
return !AerialRegex.Match([]byte(i.Main.Onair))
}
|