func ExampleEncoder() {
	enc := eventsource.NewEncoder(os.Stdout)

	events := []eventsource.Event{
		{ID: "1", Data: []byte("data")},
		{ResetID: true, Data: []byte("id reset")},
		{Type: "add", Data: []byte("1")},
	}

	for _, event := range events {
		if err := enc.Encode(event); err != nil {
			log.Fatal(err)
		}
	}

	if err := enc.WriteField("", []byte("heartbeat")); err != nil {
		log.Fatal(err)
	}

	if err := enc.Flush(); err != nil {
		log.Fatal(err)
	}

	// Output:
	// id: 1
	// data: data
	//
	// id
	// data: id reset
	//
	// event: add
	// data: 1
	//
	// : heartbeat
	//
}
Example #2
0
func main() {
	kingpin.MustParse(app.Parse(os.Args[1:]))

	templateData, err := rice.MustFindBox("static").String("index.html")
	if err != nil {
		panic(err)
	}

	indexTemplate := template.Must(template.New("index").Parse(templateData))

	latestImage := make([]imageUpdate, len(*videoDevices))
	watchers := make(map[chan imageUpdate]bool)

	for id, videoDevice := range *videoDevices {
		thisDirectory := path.Join(*imageDirectory, fmt.Sprintf("cam%d", id))

		if err := os.Mkdir(thisDirectory, 0755); err != nil && !os.IsExist(err) {
			panic(err)
		}

		go func(id int, videoDevice, thisDirectory string) {
			for {
				logrus.WithFields(logrus.Fields{
					"id":        id,
					"device":    videoDevice,
					"directory": thisDirectory,
				}).Info("opening webcam")

				err := exec.Command(
					"streamer",
					"-c", videoDevice,
					"-t", "60",
					"-r", "1",
					"-o", path.Join(thisDirectory, fmt.Sprintf("snap_%d_00.jpeg", time.Now().Unix())),
				).Run()

				if err != nil {
					logrus.WithFields(logrus.Fields{
						"id":        id,
						"device":    videoDevice,
						"directory": thisDirectory,
						"error":     err.Error(),
					}).Info("streamer crashed")
				}

				time.Sleep(time.Second * 5)
			}
		}(id, videoDevice, thisDirectory)

		go func(id int) {
			w, err := fsnotify.NewWatcher()
			if err != nil {
				panic(err)
			}
			if err := w.Watch(thisDirectory); err != nil {
				panic(err)
			}

			for ev := range w.Event {
				if ev.IsCreate() {
					latestImage[id] = imageUpdate{
						ID:   id,
						File: ev.Name,
						Time: time.Now(),
					}

					for c := range watchers {
						c <- latestImage[id]
					}
				}
			}
		}(id)
	}

	m := mux.NewRouter()

	m.Path("/").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("content-type", "text/html")
		w.WriteHeader(http.StatusOK)

		if err := indexTemplate.Execute(w, struct{ Cameras []imageUpdate }{latestImage}); err != nil {
			panic(err)
		}
	})

	m.Path("/reset").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if err := exec.Command("killall", "streamer").Run(); err != nil {
			panic(err)
		}

		var m multierror.MultiError

		var wg sync.WaitGroup
		for _, d := range *usbDevices {
			wg.Add(1)

			go func(d string) {
				defer wg.Done()

				if err := exec.Command("usbreset", d).Run(); err != nil {
					m = append(m, err)
				}
			}(d)
		}
		wg.Wait()

		if m != nil {
			panic(m)
		}
	})

	m.Path("/latest/{id:[0-9]+}.jpeg").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)

		var id int
		if idInt64, err := strconv.ParseInt(vars["id"], 10, 8); err != nil {
			panic(err)
		} else {
			id = int(idInt64)
		}

		if id >= len(*videoDevices) {
			http.Error(w, "not found", http.StatusNotFound)
			return
		}

		f, err := os.Open(latestImage[id].File)
		if err != nil {
			panic(err)
		}

		w.Header().Set("content-type", "image/jpeg")

		if _, err := io.Copy(w, f); err != nil {
			panic(err)
		}
	})

	m.Path("/stream").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		enc := eventsource.NewEncoder(w)

		w.Header().Set("content-type", "text/event-stream")
		w.WriteHeader(http.StatusOK)

		c := make(chan imageUpdate, 10)

		watchers[c] = true
		defer delete(watchers, c)

		cn := w.(http.CloseNotifier).CloseNotify()

		for {
			select {
			case e, ok := <-c:
				if !ok {
					break
				}

				f, err := os.Open(e.File)
				if err != nil {
					panic(err)
				}

				d, err := ioutil.ReadAll(f)
				if err != nil {
					panic(err)
				}

				ev := eventsource.Event{
					Type: "image",
					Data: []byte(fmt.Sprintf(
						"%d::%s::%s",
						e.ID,
						e.Time.String(),
						base64.StdEncoding.EncodeToString(d),
					)),
				}

				if err := enc.Encode(ev); err != nil {
					panic(err)
				}

				if err := enc.Flush(); err != nil {
					panic(err)
				}
			case <-cn:
				return
			case <-time.After(time.Second * 30):
				if err := enc.WriteField("heartbeat", nil); err != nil {
					panic(err)
				}
			}
		}
	})

	n := negroni.New()

	n.Use(negroni.NewRecovery())
	n.Use(negronilogrus.NewMiddleware())
	n.UseHandler(m)

	if err := http.ListenAndServe(*addr, n); err != nil {
		panic(err)
	}
}