diff --git a/player/main.go b/player/main.go index 60d0d83..d5cbfeb 100644 --- a/player/main.go +++ b/player/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "flag" "fmt" "net/http" @@ -8,30 +9,69 @@ import ( "time" ) +func getIp(r *http.Request) string { + // Get the remote address + var ip = r.RemoteAddr + + // If behind a proxy, get the real IP + if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" { + ip = fwd + } + + return ip +} + +type Viewer struct { + LastActive int64 + ActiveMirror int +} + +type ViewCountRequest struct { + ActiveMirror int `json:"active_mirror"` +} + +type ViewCountResponse struct { + TotalCount int `json:"total_count"` + ByMirror map[int]int `json:"by_mirror"` +} + func main() { var addr = flag.String("addr", ":8080", "http service address") flag.Parse() - // Map of active viewers and their last active time - var viewers = make(map[string]int64) + var viewers = make(map[string]Viewer) + var mirrorActivity = make(map[int]int) + // Handle view counting http.HandleFunc("/viewcount", func(w http.ResponseWriter, r *http.Request) { // Get the current time var now = time.Now().Unix() // Get the remote address - var ip = r.RemoteAddr + var ip = getIp(r) - // If behind a proxy, get the real IP - if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" { - ip = fwd + // Decode body JSON as a ViewerMetric + var metric ViewCountRequest + if err := json.NewDecoder(r.Body).Decode(&metric); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return } // Update the last active time - viewers[ip] = now + viewers[ip] = Viewer{ + LastActive: now, + ActiveMirror: metric.ActiveMirror, + } - // Respond with the number of viewers - fmt.Fprintf(w, "%d", len(viewers)) + // Respond + var response = ViewCountResponse{ + TotalCount: len(viewers), + ByMirror: mirrorActivity, + } + if err := json.NewEncoder(w).Encode(response); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } }) // Periodically remove inactive viewers @@ -43,8 +83,8 @@ func main() { var now = time.Now().Unix() // Remove any viewers that have not been active in the last 10 seconds - for ip, lastActive := range viewers { - if now-lastActive > 10 { + for ip, viewer := range viewers { + if now-viewer.LastActive > 10 { delete(viewers, ip) } } @@ -77,6 +117,49 @@ func main() { } }() + // Periodically aggregate viewer count per mirror + go func() { + // Prepare file to log to + var f, ferr = os.OpenFile("viewers_per_mirror.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if ferr != nil { + fmt.Println("Error opening file:", ferr) + fmt.Println("mirror statistics will not be logged") + return + } + + // Loop forever + for { + var now = time.Now().Format(time.RFC3339) + var counts = make(map[int]int) + + // Loop through all viewers + for _, viewer := range viewers { + // Increment the mirror count + counts[viewer.ActiveMirror]++ + } + + // Check if the counts have changed + var changed = false + for mirror, count := range counts { + if count != mirrorActivity[mirror] { + changed = true + break + } + } + + // If the counts have changed, log them + if changed { + for mirror, count := range counts { + fmt.Fprintf(f, "%s\t%d\t%d\n", now, mirror, count) + } + } + + // Update the mirror activity + mirrorActivity = counts + time.Sleep(time.Second) + } + }() + // Handle static files http.Handle("/", http.FileServer(http.Dir("./static"))) diff --git a/player/static/index.html b/player/static/index.html index 7a45d68..fc3244a 100644 --- a/player/static/index.html +++ b/player/static/index.html @@ -48,6 +48,29 @@ 0 viewers +
+ Selecting a mirror... +
+