mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 20:28:15 +03:00
Add basic persistent viewership stats
This commit is contained in:
parent
9ebec675b5
commit
b47ea89c06
5 changed files with 128 additions and 7 deletions
17
main.go
17
main.go
|
@ -11,14 +11,16 @@ import (
|
|||
var storage ChunkStorage
|
||||
var configuration = getConfig()
|
||||
var server *Server
|
||||
var stats *Stats
|
||||
|
||||
var online = false
|
||||
var usingExternalStorage = false
|
||||
|
||||
func main() {
|
||||
log.Println("Starting up. Please wait...")
|
||||
resetDirectories(configuration)
|
||||
checkConfig(configuration)
|
||||
stats = getSavedStats()
|
||||
stats.Setup()
|
||||
|
||||
if configuration.IPFS.Enabled {
|
||||
storage = &IPFSStorage{}
|
||||
|
@ -57,14 +59,17 @@ func startChatServer() {
|
|||
|
||||
func getStatus(w http.ResponseWriter, r *http.Request) {
|
||||
status := Status{
|
||||
Online: online,
|
||||
ViewerCount: server.ClientCount(),
|
||||
Online: stats.IsStreamConnected(),
|
||||
ViewerCount: stats.GetViewerCount(),
|
||||
OverallMaxViewerCount: stats.GetOverallMaxViewerCount(),
|
||||
SessionMaxViewerCount: stats.GetSessionMaxViewerCount(),
|
||||
}
|
||||
json.NewEncoder(w).Encode(status)
|
||||
}
|
||||
|
||||
func streamConnected() {
|
||||
online = true
|
||||
stats.StreamConnected()
|
||||
|
||||
chunkPath := configuration.PublicHLSPath
|
||||
if usingExternalStorage {
|
||||
chunkPath = configuration.PrivateHLSPath
|
||||
|
@ -73,11 +78,13 @@ func streamConnected() {
|
|||
}
|
||||
|
||||
func streamDisconnected() {
|
||||
online = false
|
||||
stats.StreamDisconnected()
|
||||
}
|
||||
|
||||
func viewerAdded() {
|
||||
stats.SetViewerCount(server.ClientCount())
|
||||
}
|
||||
|
||||
func viewerRemoved() {
|
||||
stats.SetViewerCount(server.ClientCount())
|
||||
}
|
||||
|
|
106
stats.go
Normal file
106
stats.go
Normal file
|
@ -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
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package main
|
||||
|
||||
type Status struct {
|
||||
Online bool `json:"online"`
|
||||
ViewerCount int `json:"viewerCount"`
|
||||
Online bool `json:"online"`
|
||||
ViewerCount int `json:"viewerCount"`
|
||||
OverallMaxViewerCount int `json:"overallMaxViewerCount"`
|
||||
SessionMaxViewerCount int `json:"sessionMaxViewerCount"`
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
</video>
|
||||
<div id="app">
|
||||
{{ streamStatus }} {{ viewerCount }} {{ 'viewer' | plural(viewerCount) }}.
|
||||
Max {{ sessionMaxViewerCount }} {{ 'viewer' | plural(sessionMaxViewerCount) }},
|
||||
{{ overallMaxViewerCount }} overall.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ function setupApp() {
|
|||
data: {
|
||||
streamStatus: "",
|
||||
viewerCount: 0,
|
||||
sessionMaxViewerCount: 0,
|
||||
overallMaxViewerCount: 0
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -56,6 +58,8 @@ async function getStatus() {
|
|||
: "Stream is offline."
|
||||
|
||||
app.viewerCount = status.viewerCount
|
||||
app.sessionMaxViewerCount = status.sessionMaxViewerCount
|
||||
app.overallMaxViewerCount = status.overallMaxViewerCount
|
||||
|
||||
} catch (e) {
|
||||
app.streamStatus = "Stream server is offline."
|
||||
|
|
Loading…
Reference in a new issue