package main import ( "encoding/json" "fmt" "log" "net/http" "os" "strconv" "text/template" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) type serverSettings struct { port int jsonPort int plexAddr string plexToken string plexTimeout int metricsPrefix string metricsMediaCollectingIntervalSeconds int } func envGetter(key, fallback string) string { if value, ok := os.LookupEnv(key); ok { return value } return fallback } func setupServerSettings() serverSettings { port, err := strconv.Atoi(envGetter("PORT", "9545")) if err != nil { log.Fatal("Unable to parse port string to int, failing....") } jsonPort, err := strconv.Atoi(envGetter("JSONPORT", "9546")) if err != nil { log.Fatal("Unable to parse jsonapi port string to int, failing....") } plexToken, value := os.LookupEnv("PLEX_TOKEN") if !value { log.Fatal("No plex token provided, failing....") } plexTimeout, err := strconv.Atoi(envGetter("PLEX_TIMEOUT", "10")) if err != nil { log.Fatal("Unable to parse plextimeout string to int, failing....") } metricsMediaCollectingIntervalSeconds, err := strconv.Atoi(envGetter("METRICS_MEDIA_COLLECTING_INTERVAL_SECONDS", "300")) if err != nil { log.Fatal("Unable to parse METRICS_MEDIA_COLLECTING_INTERVAL_SECONDS string to int, failing....") } return serverSettings{ port: port, jsonPort: jsonPort, plexAddr: envGetter("PLEX_ADDR", "http://localhost:32400"), plexToken: plexToken, plexTimeout: plexTimeout, metricsPrefix: envGetter("METRICS_PREFIX", "PLEX"), metricsMediaCollectingIntervalSeconds: metricsMediaCollectingIntervalSeconds, } } func setupJSONAPI(plexStatsChannel chan *plexStats, settings serverSettings) { // Create a new router mux := http.NewServeMux() tmpl := template.Must(template.ParseFiles("homepage.html")) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { tmpl.Execute(w, nil) }) // Create a handler for the `/plex-stats` endpoint mux.HandleFunc("/plex-stats", func(w http.ResponseWriter, r *http.Request) { // Create a new plexStats struct plexStats := <-plexStatsChannel // Marshal the plexStats struct to JSON jsonBody, err := json.Marshal(plexStats) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Set the content type to JSON w.Header().Set("Content-Type", "application/json") // Write the JSON body to the response w.Write(jsonBody) }) // Start the server fmt.Println("Starting JSON API Server.....") http.ListenAndServe(fmt.Sprintf(":%d", settings.jsonPort), mux) } func setStats(stats *plexStats) { plexActiveSessions.Set(stats.CurrentSessions) plexNumMovies.Set(stats.NumMovies) plexNumTVShows.Set(stats.NumTV) plexTranscodeSessions.Set(stats.NumTranscodes) plexAllSessions.Set(stats.NumAllSessions) } func main() { os.Setenv("PORT", "9545") os.Setenv("JSONPORT", "9546") os.Setenv("PLEX_ADDR", "https://plex.derajnet.duckdns.org:32400") os.Setenv("PLEX_TOKEN", "5ezJu5cjnhoAbPJRKngs") os.Setenv("PLEX_TIMEOUT", "10") os.Setenv("METRICS_PREFIX", "PLEX") os.Setenv("METRICS_MEDIA_COLLECTING_INTERVAL_SECONDS", "300") settings := setupServerSettings() // Setup plex client pc := setupClient(&settings) // Our initial gather to ensure we don't send zeros to prometheus stats := pc.gatherAllStats() setStats(stats) // Creating a data sharing channel to send same data to our json api server plexStatsChannel := make(chan *plexStats) go setupJSONAPI(plexStatsChannel, settings) go func() { for { time.Sleep(time.Second * 5) fmt.Println("Collecting info...") stats := pc.gatherAllStats() setStats(stats) // Kind of an ugly workaround? //Will cause some travel times to be up to 5 seconds as the channel in the mux waits for data to be populated? select { case plexStatsChannel <- stats: default: } } }() prometheus.MustRegister(plexActiveSessions) prometheus.MustRegister(plexNumMovies) prometheus.MustRegister(plexNumTVShows) prometheus.MustRegister(plexTranscodeSessions) prometheus.MustRegister(plexAllSessions) // m := NewMetrics(reg) fmt.Println("Startup of promethus handler complete...") http.Handle("/metrics", promhttp.Handler()) err := http.ListenAndServe(fmt.Sprintf(":%d", settings.port), nil) if err != nil { log.Fatal("Unable to start the prometheus handler....") } }