func (repo *RepositoryConfig) GenerateTimelapse(date time.Time, camera *Camera, kind MediaKind) {
	TAG := "RepositoryConfig.GenerateTimelapse"

	start := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
	end := time.Date(date.Year(), date.Month(), date.Day()+1, 0, 0, 0, 0, date.Location())
	log.Debug(TAG, "date range", date, start, end)

	if kind != MediaCollected && kind != MediaMotion {
		panic(fmt.Errorf("cannot generate timelapse for '%s' content", kind))
	}

	if camera.Diurnal {
		_, start, end = camera.LocalDaylight(date)
	}

	var next time.Time
	images := repo.ListKind(camera.ID, kind)
	candidates := []*Image{}
	for _, img := range images {
		if img.Timestamp.Before(start) || end.Before(img.Timestamp) {
			continue
		}
		candidates = append(candidates, img)
	}
	sort.Slice(candidates, func(i, j int) bool { return candidates[i].Timestamp.Before(candidates[j].Timestamp) })
	images = []*Image{}
	names := []string{}
	for _, img := range candidates {
		if img.Timestamp.Before(next) {
			continue
		} else {
			next = img.Timestamp.Add(29 * time.Second) // no more than 2 frames per minute
		}
		images = append(images, img)
		dataPath := repo.dataPath(img.Source, fmt.Sprintf("%s.jpg", img.Handle))
		names = append(names, dataPath)
	}

	// images now contains a sorted list of all files that should be in the timelapse
	if len(images) < 1 {
		log.Warn(TAG, "no images from which to generate timelapse")
		return
	}

	// create a temp dir to generate our timelapse in via shelling out to mencoder
	dir, err := ioutil.TempDir("/tmp", "timelapse-")
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(dir); err != nil {
			log.Error(TAG, fmt.Sprintf("failed to remove tempdir '%s'", dir), err)
		}
	}()

	// create a file listing other files to include in the timelapse, to be passed to mencoder
	indexPath := path.Join(dir, "index")
	if err := ioutil.WriteFile(indexPath, []byte(strings.Join(names, "\n")), 0600); err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(indexPath); err != nil {
			log.Error(TAG, fmt.Sprintf("failed to remove index '%s'", indexPath), err)
		}
	}()

	args := "mf://@%s -mf fps=24 -o %s -of lavf -ovc lavc -lavfopts format=webm -lavcopts threads=8:vcodec=libvpx -ffourcc VP80"
	webm := path.Join(dir, "generated.webm")
	args = fmt.Sprintf(args, indexPath, webm)
	cmd := exec.Command("mencoder", strings.Split(args, " ")...)
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(webm); err != nil {
			log.Error(TAG, fmt.Sprintf("failed to remove generated '%s'", webm), err)
		}
	}()
	log.Debug(TAG, fmt.Sprintf("started mencoder for '%s' with args", camera.ID), args)
	if err := cmd.Wait(); err != nil {
		panic(err)
	}
	log.Debug(TAG, fmt.Sprintf("mencoder complete for '%s'", webm))

	webmBytes, err := ioutil.ReadFile(webm)
	if err != nil {
		panic(err)
	}

	still := images[len(images)/2] // already checked len(images) > 0
	var buf bytes.Buffer
	still.Retrieve(&buf)
	stillBytes := buf.Bytes()

	img := repo.Store(camera.ID, stillBytes)
	if img == nil {
		log.Warn(TAG, fmt.Sprintf("nonerror result but nil image"))
	} else {
		img.LinkVideo(webmBytes)
	}
	img.Pin(MediaGenerated)

	log.Status(TAG, fmt.Sprintf("generated timelapse for '%s' from %d images", camera.ID, len(images)))
}
// GenerateTimelapse scans for all files matching the indicated source and kind
// taken during the indicated date, and generates a timelapse from them. If
// `diurnal` is true, it uses astronomical sunrise & sunset to limit the
// timelapse to only daylight hours.
GenerateTimelapse scans for all files matching the indicated source and kind
taken during the indicated date, and generates a timelapse from them. If
`diurnal` is true, it uses astronomical sunrise & sunset to limit the
timelapse to only daylight hours.
// GenerateTimelapse scans for all files matching the indicated source and kind // taken during the indicated date and generates a timelapse from them . If // ` diurnal ` is true it uses astronomical sunrise & sunset to limit the // timelapse to only daylight hours .
func (repo *RepositoryConfig) GenerateTimelapse(date time.Time, camera *Camera, kind MediaKind) {
	TAG := "RepositoryConfig.GenerateTimelapse"
	start := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
	end := time.Date(date.Year(), date.Month(), date.Day()+1, 0, 0, 0, 0, date.Location())
	log.Debug(TAG, "date range", date, start, end)
	if kind != MediaCollected && kind != MediaMotion {
		panic(fmt.Errorf("cannot generate timelapse for '%s' content", kind))
	}
	if camera.Diurnal {
		_, start, end = camera.LocalDaylight(date)
	}
	var next time.Time
	images := repo.ListKind(camera.ID, kind)
	candidates := []*Image{}
	for _, img := range images {
		if img.Timestamp.Before(start) || end.Before(img.Timestamp) {
			continue
		}
		candidates = append(candidates, img)
	}
	sort.Slice(candidates, func(i, j int) bool { return candidates[i].Timestamp.Before(candidates[j].Timestamp) })
	images = []*Image{}
	names := []string{}
	for _, img := range candidates {
		if img.Timestamp.Before(next) {
			continue
		} else {
			next = img.Timestamp.Add(29 * time.Second) 
		}
		images = append(images, img)
		dataPath := repo.dataPath(img.Source, fmt.Sprintf("%s.jpg", img.Handle))
		names = append(names, dataPath)
	}
	if len(images) < 1 {
		log.Warn(TAG, "no images from which to generate timelapse")
		return
	}
	dir, err := ioutil.TempDir("/tmp", "timelapse-")
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(dir); err != nil {
			log.Error(TAG, fmt.Sprintf("failed to remove tempdir '%s'", dir), err)
		}
	}()
	indexPath := path.Join(dir, "index")
	if err := ioutil.WriteFile(indexPath, []byte(strings.Join(names, "\n")), 0600); err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(indexPath); err != nil {
			log.Error(TAG, fmt.Sprintf("failed to remove index '%s'", indexPath), err)
		}
	}()
	args := "mf://@%s -mf fps=24 -o %s -of lavf -ovc lavc -lavfopts format=webm -lavcopts threads=8:vcodec=libvpx -ffourcc VP80"
	webm := path.Join(dir, "generated.webm")
	args = fmt.Sprintf(args, indexPath, webm)
	cmd := exec.Command("mencoder", strings.Split(args, " ")...)
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(webm); err != nil {
			log.Error(TAG, fmt.Sprintf("failed to remove generated '%s'", webm), err)
		}
	}()
	log.Debug(TAG, fmt.Sprintf("started mencoder for '%s' with args", camera.ID), args)
	if err := cmd.Wait(); err != nil {
		panic(err)
	}
	log.Debug(TAG, fmt.Sprintf("mencoder complete for '%s'", webm))
	webmBytes, err := ioutil.ReadFile(webm)
	if err != nil {
		panic(err)
	}
	still := images[len(images)/2] 
	var buf bytes.Buffer
	still.Retrieve(&buf)
	stillBytes := buf.Bytes()
	img := repo.Store(camera.ID, stillBytes)
	if img == nil {
		log.Warn(TAG, fmt.Sprintf("nonerror result but nil image"))
	} else {
		img.LinkVideo(webmBytes)
	}
	img.Pin(MediaGenerated)
	log.Status(TAG, fmt.Sprintf("generated timelapse for '%s' from %d images", camera.ID, len(images)))
}
func ( repo * RepositoryConfig ) GenerateTimelapse ( date time . Time , camera * Camera , kind MediaKind ) { TAG := " " start := time . Date ( date . Year ( ) , date . Month ( ) , date . Day ( ) , 0 , 0 , 0 , 0 , date . Location ( ) ) end := time . Date ( date . Year ( ) , date . Month ( ) , date . Day ( ) + 1 , 0 , 0 , 0 , 0 , date . Location ( ) ) log . Debug ( TAG , " " , date , start , end ) if kind != MediaCollected && kind != MediaMotion { panic ( fmt . Errorf ( " " , kind ) ) } if camera . Diurnal { _ , start , end = camera . LocalDaylight ( date ) } var next time . Time images := repo . ListKind ( camera . ID , kind ) candidates := [ ] * Image { } for _ , img := range images { if img . Timestamp . Before ( start ) || end . Before ( img . Timestamp ) { continue } candidates = append ( candidates , img ) } sort . Slice ( candidates , func ( i , j int ) bool { return candidates [ i ] . Timestamp . Before ( candidates [ j ] . Timestamp ) } ) images = [ ] * Image { } names := [ ] string { } for _ , img := range candidates { if img . Timestamp . Before ( next ) { continue } else { next = img . Timestamp . Add ( 29 * time . Second ) } images = append ( images , img ) dataPath := repo . dataPath ( img . Source , fmt . Sprintf ( " " , img . Handle ) ) names = append ( names , dataPath ) } if len ( images ) < 1 { log . Warn ( TAG , " " ) return } dir , err := ioutil . TempDir ( " " , " " ) if err != nil { panic ( err ) } defer func ( ) { if err := os . Remove ( dir ) ; err != nil { log . Error ( TAG , fmt . Sprintf ( " " , dir ) , err ) } } ( ) indexPath := path . Join ( dir , " " ) if err := ioutil . WriteFile ( indexPath , [ ] byte ( strings . Join ( names , " \n " ) ) , 0600 ) ; err != nil { panic ( err ) } defer func ( ) { if err := os . Remove ( indexPath ) ; err != nil { log . Error ( TAG , fmt . Sprintf ( " " , indexPath ) , err ) } } ( ) args := " " webm := path . Join ( dir , " " ) args = fmt . Sprintf ( args , indexPath , webm ) cmd := exec . Command ( " " , strings . Split ( args , " " ) ... ) if err := cmd . Start ( ) ; err != nil { panic ( err ) } defer func ( ) { if err := os . Remove ( webm ) ; err != nil { log . Error ( TAG , fmt . Sprintf ( " " , webm ) , err ) } } ( ) log . Debug ( TAG , fmt . Sprintf ( " " , camera . ID ) , args ) if err := cmd . Wait ( ) ; err != nil { panic ( err ) } log . Debug ( TAG , fmt . Sprintf ( " " , webm ) ) webmBytes , err := ioutil . ReadFile ( webm ) if err != nil { panic ( err ) } still := images [ len ( images ) / 2 ] var buf bytes . Buffer still . Retrieve ( & buf ) stillBytes := buf . Bytes ( ) img := repo . Store ( camera . ID , stillBytes ) if img == nil { log . Warn ( TAG , fmt . Sprintf ( " " ) ) } else { img . LinkVideo ( webmBytes ) } img . Pin ( MediaGenerated ) log . Status ( TAG , fmt . Sprintf ( " " , camera . ID , len ( images ) ) ) }
GenerateTimelapse scans for all files matching the indicated source and kind
taken during the indicated date, and generates a timelapse from them.
GenerateTimelapse scans for all files matching the indicated source and kind taken during the indicated date and generates a timelapse from them .
// no more than 2 frames per minute // images now contains a sorted list of all files that should be in the timelapse // create a temp dir to generate our timelapse in via shelling out to mencoder // create a file listing other files to include in the timelapse, to be passed to mencoder // already checked len(images) > 0