package main import ( "encoding/json" "image" "image/color" "io/ioutil" "log" "net/http" "path/filepath" "strconv" "text/template" "time" ) var baseHTML = filepath.Join("templates", "base.html") var indexHTML = filepath.Join("templates", "index.html") var addStreamHTML = filepath.Join("templates", "add_stream.html") var streamHTML = filepath.Join("templates", "stream.html") var momentsHTML = filepath.Join("templates", "moments.html") // Server is the main application struct type Server struct { Streams map[string]*Stream } func (server Server) index(w http.ResponseWriter, r *http.Request) { if r.FormValue("name") == "" { indexTemplate, err := template.ParseFiles(indexHTML, baseHTML) if err != nil { log.Println(err) return } indexTemplate.Execute(w, server) return } name := r.FormValue("name") stream, exists := server.Streams[name] if !exists { return } streamTemplate, err := template.ParseFiles(streamHTML, baseHTML) watchAreasJSON, _ := json.Marshal(stream.WatchAreas) if err != nil { log.Println(err) return } streamTemplate.Execute(w, struct { Streams map[string]*Stream Stream *Stream WatchAreasJSON string }{ server.Streams, stream, string(watchAreasJSON[:]), }) } func (server Server) addStream(w http.ResponseWriter, r *http.Request) { nameMessage := "" URLMessage := "" intervalMessage := "" motionIntervalMessage := "" if r.Method == "POST" { name := r.FormValue("name") URL := r.FormValue("URL") interval := r.FormValue("interval") motionInterval := r.FormValue("motion_interval") if name == "" { nameMessage = "Name is required" } if URL == "" { URLMessage = "URL is required" } if interval == "" { intervalMessage = "Interval is required" } if motionInterval == "" { motionIntervalMessage = "MotionInterval is required" } _, exists := server.Streams[name] if exists { nameMessage = "Name already used" } if !exists && name != "" && URL != "" && interval != "" && motionInterval != "" { intInterval, err := strconv.ParseInt(interval, 10, 64) if err != nil { log.Println("Illigal value for Interval: ", interval) intInterval = 5000 } intMotionInterval, err := strconv.ParseInt(motionInterval, 10, 64) if err != nil { log.Println("Illigal value for MotionInterval: ", motionInterval) intMotionInterval = 1000 } server.Streams[name] = NewStream( name, URL, (int)(intInterval), (int)(intMotionInterval), ) go server.Streams[name].UpdateInterval() http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } } addStreamTemplate, err := template.ParseFiles(addStreamHTML, baseHTML) if err != nil { log.Println(err) return } addStreamTemplate.Execute(w, struct { Streams map[string]*Stream NameMessage string URLMessage string IntervalMessage string MotionIntervalMessage string }{ server.Streams, nameMessage, URLMessage, intervalMessage, motionIntervalMessage, }) } func (server Server) addWatchArea(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { return } streamName := r.FormValue("streamName") if streamName == "" { return } stream, exists := server.Streams[streamName] if !exists { return } name := r.FormValue("name") if name == "" { return } x0 := r.FormValue("x0") if x0 == "" { return } y0 := r.FormValue("y0") if y0 == "" { return } x1 := r.FormValue("x1") if x1 == "" { return } y1 := r.FormValue("y1") if y1 == "" { return } R := r.FormValue("R") if R == "" { return } G := r.FormValue("G") if G == "" { return } B := r.FormValue("B") if B == "" { return } stream.AddWatchArea( name, image.Rect( StrToInt(x0), StrToInt(y0), StrToInt(x1), StrToInt(y1), ), color.RGBA{ uint8(StrToInt(R)), uint8(StrToInt(G)), uint8(StrToInt(B)), 255, }, ) http.Redirect(w, r, "/?name="+streamName, http.StatusTemporaryRedirect) } func (server Server) streamRecordings(w http.ResponseWriter, r *http.Request) { streamName := r.FormValue("stream") if streamName == "" { return } watchAreaName := r.FormValue("watchArea") if watchAreaName == "" { return } stream, exists := server.Streams[streamName] if !exists { return } watchArea, exists := stream.GetWatchAreaByName(watchAreaName) if !exists { return } images, err := ioutil.ReadDir(watchArea.GetWatchAreaStoreDir(*stream)) if err != nil { log.Println("Could not read watchArea directory", err) return } moments := make([][]string, 0) currentMoment := make([]string, 0) momentIndex := 0 foundMoment := false for i := range images { if i == 0 { continue } previousImage := images[i-1] previousTime, err := time.Parse(timeLayout, previousImage.Name()) if err != nil { log.Println("Can't parse: ", previousImage.Name()) log.Println(err) continue } currentImage := images[i] currentTime, err := time.Parse(timeLayout, currentImage.Name()) if err != nil { log.Println("Can't parse: ", currentImage.Name()) log.Println(err) continue } timeDifference := currentTime.Sub(previousTime) if timeDifference < (time.Duration(stream.Interval) * time.Millisecond) { foundMoment = true currentMoment = append(currentMoment, previousImage.Name()) } else { if foundMoment { currentMoment = append(currentMoment, currentImage.Name()) moments = append(moments, currentMoment) currentMoment = make([]string, 0) momentIndex++ } foundMoment = false } } momentsTemplate, err := template.ParseFiles(momentsHTML, baseHTML) if err != nil { log.Println(err) return } momentsJSON, _ := json.Marshal(moments) err = momentsTemplate.Execute(w, struct { Streams map[string]*Stream MomentsJSON string Moments [][]string Stream *Stream WatchArea *WatchArea }{ server.Streams, string(momentsJSON[:]), moments, stream, watchArea, }) } func main() { staticFileServer := http.FileServer(http.Dir("./static")) http.Handle("/static/", http.StripPrefix("/static/", staticFileServer)) streamFileServer := http.FileServer(http.Dir("./streams")) http.Handle("/streams/", http.StripPrefix("/streams/", streamFileServer)) server := Server{ Streams: make(map[string]*Stream), } streams, err := ioutil.ReadDir(GetStreamDirPath()) if err != nil { log.Fatal("Could not read Streamdir") } for _, streamStoreDir := range streams { if !streamStoreDir.IsDir() { continue } streamJSONPath := filepath.Join(GetStreamDirPath(), streamStoreDir.Name(), "stream.json") stream := StreamFromJSON(streamJSONPath) server.Streams[stream.Name] = stream go stream.UpdateInterval() } http.HandleFunc("/", server.index) http.HandleFunc("/addStream", server.addStream) http.HandleFunc("/addWatchArea", server.addWatchArea) http.HandleFunc("/streamRecordings", server.streamRecordings) log.Println("Starting StreamWatcher Server on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }