aboutsummaryrefslogtreecommitdiff
path: root/internal/pvfm/info.go
blob: 4c3456e5122e3248aade16e22e739ae661cf791a (plain)
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))
}