mirror of
https://github.com/owncast/owncast.git
synced 2024-11-22 04:40:37 +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 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
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
|
||||||
|
}
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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…
Reference in a new issue