118 lines
4.2 KiB
Go
118 lines
4.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/deranjer/gvc/common/database"
|
|
watcher "github.com/radovskyb/watcher"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
type key string
|
|
type Event struct {
|
|
Name string
|
|
Progress int
|
|
Total int
|
|
}
|
|
|
|
// The watcher is responsible for not only seeing when a file changes,
|
|
// but also keeping track of
|
|
// * the file hash so that if it changes again any modifications can be handled
|
|
// * copying any versions and keeping them safe (even if temporary)
|
|
// * creating the diff of the file, in both directions if necessary
|
|
// * storing the details in the database
|
|
func NewWatcher(logger *zerolog.Logger, KEYFOLDER, DOWNLOADFOLDER, SYNCFOLDER, THUMBFOLDER, DIFFFOLDER string) (FileWatcher, error) {
|
|
w := FileWatcher{
|
|
watcher.New(),
|
|
logger,
|
|
true, //used to temporarily ignore events if necessary
|
|
KEYFOLDER, DOWNLOADFOLDER, SYNCFOLDER, THUMBFOLDER, DIFFFOLDER,
|
|
}
|
|
return w, nil
|
|
}
|
|
|
|
func (fw *FileWatcher) Ignore() bool {
|
|
fw.Enabled = false
|
|
return fw.Enabled
|
|
}
|
|
func (fw *FileWatcher) Enable() bool {
|
|
fw.Enabled = true
|
|
return fw.Enabled
|
|
}
|
|
func (fw *FileWatcher) IsEnabled() bool {
|
|
return fw.Enabled
|
|
}
|
|
|
|
// BeginWatcherRoutine kicks off the watcher. When the watcher noticies a file change,
|
|
// certain actions will be taken in case of event and error
|
|
// the routine will handle these whenever this occurs.
|
|
// If certain functions need to be called then this will
|
|
// need to be specified as part of the managers lambda functions
|
|
// TODO: Should return an error
|
|
func (fw *FileWatcher) BeginWatcherRoutine(ctx context.Context, wg *sync.WaitGroup, diffChannel chan database.DiffObject, onFileChanged func(string) (database.File, error)) {
|
|
|
|
//seems a bit barking, but we can now cancel any diff that is occuring on a file when it fires again
|
|
cancelFunctions := make(map[string]func())
|
|
for {
|
|
select {
|
|
// we have filtered already on the [Op]erations we want to listen for so no need to check here
|
|
case event := <-fw.Watcher.Event:
|
|
if !fw.IsEnabled() {
|
|
fw.Info().Msgf("ignoring event and reenabling the watcher %s\r\n", event)
|
|
fw.Enable()
|
|
continue
|
|
}
|
|
fw.Info().Msgf("event fired ", event)
|
|
//this is currently slow as it does a db lookup on the path.
|
|
//TODO: On load (or whenever a file is added to the watcher, the db information for files being watched, could be cached in memory. This would be much faster)
|
|
fileInfo, err := onFileChanged(event.Path) //could return the 'Event' object here
|
|
syncFilePath := fileInfo.CurrentBase
|
|
//uniqueName := fileInfo.Unique
|
|
|
|
// fileID := fileInfo.ID
|
|
//we need the hash of the current base, not the hash of the original file
|
|
// fileHash := fileInfo.CurrentHash //hash needs to come from
|
|
if err != nil {
|
|
fw.Err(err).Msg("path was not returned to sync path")
|
|
continue
|
|
}
|
|
//cancel the event if it indeed is running...
|
|
if cancelFunctions[event.Path] != nil {
|
|
cancelFunctions[event.Path]()
|
|
delete(cancelFunctions, event.Path)
|
|
}
|
|
|
|
//context for the current event. Calling cancel will cancel the routines
|
|
//to kill a context you must have access to the cancel function.
|
|
// i could add the cancel function to a map of them
|
|
// if you want to kill it you call on the correct cancel function
|
|
// that will kill the context. Fine.
|
|
// If however you want to kill it from another place....
|
|
// then you need a cancel channel, which inturn calls the equivelent cancel function.... ok
|
|
// sounds about best i can do right now...
|
|
// kind of bonkers right....
|
|
cancelContext, cancel := context.WithCancel(ctx)
|
|
cancelFunctions[event.Path] = cancel
|
|
// good idea to not use strings as keys directly as can conflict across namespaces
|
|
// this needs to be sorted out -- too many things called an event....
|
|
// TODO: its totally bananas
|
|
e := Event{
|
|
Name: event.Path,
|
|
Progress: 0,
|
|
Total: 100,
|
|
}
|
|
eventContext := context.WithValue(cancelContext, key(event.Path), e)
|
|
if err := manageFileDiffing(eventContext, event.Path, syncFilePath, fw.DIFFFOLDER, true, diffChannel, wg); err != nil {
|
|
// I don't think this can be reached...
|
|
fw.Warn().Msgf("Error managing the diffing process %s", err)
|
|
}
|
|
case err := <-fw.Watcher.Error:
|
|
fw.Err(err)
|
|
case <-fw.Watcher.Closed:
|
|
//fw.Notice("radovskyb closed")
|
|
return
|
|
}
|
|
}
|
|
}
|