Skip to content

Instantly share code, notes, and snippets.

@fabstu
Last active August 24, 2020 11:14
Show Gist options
  • Save fabstu/68a627386b502d5b5fc01e8980365a2e to your computer and use it in GitHub Desktop.
Save fabstu/68a627386b502d5b5fc01e8980365a2e to your computer and use it in GitHub Desktop.
Screenreader with go
// Package takes screenshots.
package main
import (
"context"
"os"
"github.com/spf13/cobra"
)
// RootCMD creates a root command of a program.
func RootCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "screenreader",
Short: "takes screenshots periodically",
}
cfgFlags := registerFlags(cmd)
cmd.RunE = func(cmd *cobra.Command, args []string) (err error) {
cfg, err := cfgFlags.makeSettings()
if err != nil {
return
}
return cfg.loop(context.Background())
}
cmd.SetOut(os.Stdout)
cmd.SetErr(os.Stderr)
cmd.AddCommand()
return cmd
}
func main() {
errorExitCode := 1
if err := RootCMD().Execute(); err != nil {
// if !helper.ContainsDoNotPrintHelp(err) {
// fmt.Println(err)
// }
os.Exit(errorExitCode)
}
}
package main
import (
"context"
"fmt"
"image"
"image/png"
"os"
"path/filepath"
"time"
scrn "github.com/kbinani/screenshot"
"github.com/spf13/cobra"
"k8s.io/klog/v2"
)
type settings struct {
framerate int
saveLocation string
}
type settingsFlags struct {
framerate *int
saveLocation *string
}
var errOutDirectory = fmt.Errorf("error: out must be directory")
func registerFlags(cmd *cobra.Command) *settingsFlags {
return &settingsFlags{
framerate: cmd.Flags().Int("framerate", 24, "framerate to capture screenshots with"),
saveLocation: cmd.Flags().String("out", ".screenshots", "where to stores the screenshots"),
}
}
func (cfgFlags *settingsFlags) makeSettings() (cfg *settings, err error) {
s, err := os.Stat(*cfgFlags.saveLocation)
if err != nil {
return
}
if !s.IsDir() {
return nil, errOutDirectory
}
return &settings{
framerate: *cfgFlags.framerate,
saveLocation: *cfgFlags.saveLocation,
}, nil
}
func (cfg settings) loop(ctx context.Context) (err error) {
durationBetweenScreenshots := time.Second / time.Duration(cfg.framerate)
klog.InfoS("starting screenshot capture loop",
"settings", fmt.Sprintf("%#v", cfg),
"duration between screenshots", durationBetweenScreenshots,
)
// get main screen bounds
bounds := scrn.GetDisplayBounds(0)
ticker := time.NewTicker(durationBetweenScreenshots)
defer func() {
ticker.Stop()
}()
ctx, cancel := context.WithCancel(ctx)
defer func() {
cancel()
}()
for x := 0; ; x++ {
n := x
waitingForTickStart := time.Now()
select {
case <-ctx.Done():
return
case t := <-ticker.C:
go func() {
start := time.Now()
ssSettings := &screenShotSettings{
outFile: filepath.Join(
cfg.saveLocation,
fmt.Sprintf("%05d.png", n),
),
bounds: bounds,
}
klog.InfoS("Making screenshot",
"n", n,
"time_waiting_to_tick", time.Since(waitingForTickStart),
"tick-at", t,
"screenshot_settings", fmt.Sprintf("%#v", ssSettings),
)
err := takeScreenshot(ssSettings)
if err != nil {
klog.ErrorS(err, "screenshot failed",
"n", n,
"screenshot_settings", ssSettings,
)
cancel()
return
}
klog.InfoS("screenshot done",
"n", n,
"duration", time.Since(start),
)
}()
}
}
}
type screenShotSettings struct {
outFile string
bounds image.Rectangle
}
func takeScreenshot(cfg *screenShotSettings) (err error) {
// Take screenshot
img, err := scrn.CaptureRect(cfg.bounds)
if err != nil {
return err
}
// Create output file
f, err := os.Create(cfg.outFile)
if err != nil {
return err
}
defer f.Close()
// Encode screenshot
err = png.Encode(f, img)
if err != nil {
return err
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment