Browse Source

Add basic persistent viewership stats

pull/5/head
Gabe Kangas 5 years ago
parent
commit
b47ea89c06
  1. 17
      main.go
  2. 106
      stats.go
  3. 2
      status.go
  4. 2
      webroot/index.html
  5. 4
      webroot/js/app.js

17
main.go

@ -11,14 +11,16 @@ import (
var storage ChunkStorage var storage ChunkStorage
var configuration = getConfig() var configuration = getConfig()
var server *Server var server *Server
var stats *Stats
var online = false
var usingExternalStorage = false var usingExternalStorage = false
func main() { func main() {
log.Println("Starting up. Please wait...") log.Println("Starting up. Please wait...")
resetDirectories(configuration) resetDirectories(configuration)
checkConfig(configuration) checkConfig(configuration)
stats = getSavedStats()
stats.Setup()
if configuration.IPFS.Enabled { if configuration.IPFS.Enabled {
storage = &IPFSStorage{} storage = &IPFSStorage{}
@ -57,14 +59,17 @@ func startChatServer() {
func getStatus(w http.ResponseWriter, r *http.Request) { func getStatus(w http.ResponseWriter, r *http.Request) {
status := Status{ status := Status{
Online: online, Online: stats.IsStreamConnected(),
ViewerCount: server.ClientCount(), ViewerCount: stats.GetViewerCount(),
OverallMaxViewerCount: stats.GetOverallMaxViewerCount(),
SessionMaxViewerCount: stats.GetSessionMaxViewerCount(),
} }
json.NewEncoder(w).Encode(status) json.NewEncoder(w).Encode(status)
} }
func streamConnected() { func streamConnected() {
online = true stats.StreamConnected()
chunkPath := configuration.PublicHLSPath chunkPath := configuration.PublicHLSPath
if usingExternalStorage { if usingExternalStorage {
chunkPath = configuration.PrivateHLSPath chunkPath = configuration.PrivateHLSPath
@ -73,11 +78,13 @@ func streamConnected() {
} }
func streamDisconnected() { func streamDisconnected() {
online = false stats.StreamDisconnected()
} }
func viewerAdded() { func viewerAdded() {
stats.SetViewerCount(server.ClientCount())
} }
func viewerRemoved() { func viewerRemoved() {
stats.SetViewerCount(server.ClientCount())
} }

106
stats.go

@ -0,0 +1,106 @@
package main
import (
"encoding/json"
"io/ioutil"
"math"
"os"
"time"
)
type Stats struct {
streamConnected bool `json:"-"`
ViewerCount int `json:"viewerCount"`
SessionMaxViewerCount int `json:"sessionMaxViewerCount"`
OverallMaxViewerCount int `json:"overallMaxViewerCount"`
LastDisconnectTime time.Time `json:"lastDisconnectTime"`
}
func (s *Stats) Setup() {
ticker := time.NewTicker(2 * time.Minute)
quit := make(chan struct{})
go func() {
for {
select {
case <-ticker.C:
s.save()
case <-quit:
ticker.Stop()
return
}
}
}()
}
func (s *Stats) IsStreamConnected() bool {
return s.streamConnected
}
func (s *Stats) SetViewerCount(count int) {
s.ViewerCount = count
s.SessionMaxViewerCount = int(math.Max(float64(s.ViewerCount), float64(s.SessionMaxViewerCount)))
s.OverallMaxViewerCount = int(math.Max(float64(s.SessionMaxViewerCount), float64(s.OverallMaxViewerCount)))
}
func (s *Stats) GetViewerCount() int {
return s.ViewerCount
}
func (s *Stats) GetSessionMaxViewerCount() int {
return s.SessionMaxViewerCount
}
func (s *Stats) GetOverallMaxViewerCount() int {
return s.OverallMaxViewerCount
}
func (s *Stats) ViewerConnected() {
}
func (s *Stats) ViewerDisconnected() {
}
func (s *Stats) StreamConnected() {
s.streamConnected = true
timeSinceDisconnect := time.Since(s.LastDisconnectTime).Minutes()
if timeSinceDisconnect > 15 {
s.SessionMaxViewerCount = 0
}
}
func (s *Stats) StreamDisconnected() {
s.streamConnected = false
s.LastDisconnectTime = time.Now()
}
func (s *Stats) save() {
jsonData, err := json.Marshal(&s)
verifyError(err)
f, err := os.Create("config/stats.json")
defer f.Close()
verifyError(err)
_, err = f.Write(jsonData)
verifyError(err)
}
func getSavedStats() *Stats {
filePath := "config/stats.json"
if !fileExists(filePath) {
return &Stats{}
}
jsonFile, err := ioutil.ReadFile(filePath)
var stats Stats
err = json.Unmarshal(jsonFile, &stats)
if err != nil {
panic(err)
}
return &stats
}

2
status.go

@ -3,4 +3,6 @@ package main
type Status struct { type Status struct {
Online bool `json:"online"` Online bool `json:"online"`
ViewerCount int `json:"viewerCount"` ViewerCount int `json:"viewerCount"`
OverallMaxViewerCount int `json:"overallMaxViewerCount"`
SessionMaxViewerCount int `json:"sessionMaxViewerCount"`
} }

2
webroot/index.html

@ -41,6 +41,8 @@
</video> </video>
<div id="app"> <div id="app">
{{ streamStatus }} {{ viewerCount }} {{ 'viewer' | plural(viewerCount) }}. {{ streamStatus }} {{ viewerCount }} {{ 'viewer' | plural(viewerCount) }}.
Max {{ sessionMaxViewerCount }} {{ 'viewer' | plural(sessionMaxViewerCount) }},
{{ overallMaxViewerCount }} overall.
</div> </div>
</div> </div>

4
webroot/js/app.js

@ -12,6 +12,8 @@ function setupApp() {
data: { data: {
streamStatus: "", streamStatus: "",
viewerCount: 0, viewerCount: 0,
sessionMaxViewerCount: 0,
overallMaxViewerCount: 0
}, },
}); });
@ -56,6 +58,8 @@ async function getStatus() {
: "Stream is offline." : "Stream is offline."
app.viewerCount = status.viewerCount app.viewerCount = status.viewerCount
app.sessionMaxViewerCount = status.sessionMaxViewerCount
app.overallMaxViewerCount = status.overallMaxViewerCount
} catch (e) { } catch (e) {
app.streamStatus = "Stream server is offline." app.streamStatus = "Stream server is offline."

Loading…
Cancel
Save