// Package filewite adds a function to write the data stream to a file package filewrite import ( "context" "fmt" "os" "path" "time" "github.com/chathaway-codes/home-sensors/v2/internal/h264video" "github.com/chathaway-codes/home-sensors/v2/internal/watcher/config" "github.com/rs/zerolog/log" ) var Default = &Mod{} type FileWrite struct { cfg *config.Config cancelFunc func() ctx context.Context video *h264video.Video } func New(video *h264video.Video, cfg *config.Config) (*FileWrite, error) { ctx, cancelFunc := context.WithCancel(context.Background()) return &FileWrite{ cfg: cfg, cancelFunc: cancelFunc, video: video, ctx: ctx, }, nil } // Run launches the commands and begins creating data. It will block until Done is called. func (v *FileWrite) Run() { if v.cfg.LocalCopy == "" { // Feature is disabled; close right away return } // Confirm that the target directory is a directory stat, err := os.Stat(v.cfg.LocalCopy) log := log.With().Str("directory", v.cfg.LocalCopy).Logger() if err != nil { log.Fatal().Err(err).Msg("failed to open path") } if !stat.IsDir() { log.Fatal().Msg("path is not directory") } // Start snooping videoCh, codec, done := v.video.Join() defer done() // TODO: what do with codec? log.Info().Str("codec", codec).Msg("got video codec") now := time.Now() fileName := path.Join(v.cfg.LocalCopy, fmt.Sprintf("%s.h264", now.Format("2006-01-02T15:04:05"))) writer, err := os.Create(fileName) if err != nil { log.Fatal().Err(err).Str("file", fileName).Msg("failed to create file") } for bytes := range videoCh { select { case <-v.ctx.Done(): return default: // do nothing } _, err := writer.Write(bytes) if err != nil { log.Fatal().Str("file", fileName).Err(err).Msg("failed to write bytes") } // Split it every half hour or so if time.Since(now) > time.Minute*30 { now = time.Now() if err := writer.Close(); err != nil { log.Fatal().Err(err).Msg("failed to close file") } fileName := path.Join(v.cfg.LocalCopy, fmt.Sprintf("%s.h264", now.Format("2006-01-02T15:04:05"))) writer, err = os.Create(fileName) if err != nil { log.Fatal().Err(err).Str("file", fileName).Msg("failed to create file") } } } } // Done stops the processing. func (v *FileWrite) Done() { v.cancelFunc() } type Mod struct{} func (m *Mod) Get(video *h264video.Video) (*FileWrite, error) { cfg, err := config.Default.Get() if err != nil { return nil, err } return New(video, cfg) }