From: Matt Joiner Date: Fri, 22 Aug 2014 07:31:03 +0000 (+1000) Subject: Simplify dirwatch to scan the whole directory on changes X-Git-Tag: v1.0.0~1621 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=60df4e100c6b0c2aea3d583f5bd55eee2a4aaa5e;p=btrtrc.git Simplify dirwatch to scan the whole directory on changes --- diff --git a/cmd/torrentfs/main.go b/cmd/torrentfs/main.go index 63ec59df..1b3d9797 100644 --- a/cmd/torrentfs/main.go +++ b/cmd/torrentfs/main.go @@ -128,8 +128,8 @@ func main() { if err != nil { log.Printf("error adding torrent to client: %s", err) } - } else if ev.Magnet != "" { - err := client.AddMagnet(ev.Magnet) + } else if ev.MagnetURI != "" { + err := client.AddMagnet(ev.MagnetURI) if err != nil { log.Printf("error adding magnet: %s", err) } diff --git a/util/dirwatch/dirwatch.go b/util/dirwatch/dirwatch.go index 05458217..afc7653d 100644 --- a/util/dirwatch/dirwatch.go +++ b/util/dirwatch/dirwatch.go @@ -1,13 +1,18 @@ +// Package dirwatch provides filesystem-notification based tracking of torrent +// info files and magnet URIs in a directory. package dirwatch import ( - "bitbucket.org/anacrolix/go.torrent" "bufio" - "github.com/anacrolix/libtorgo/metainfo" - "github.com/go-fsnotify/fsnotify" "log" "os" "path/filepath" + + "bitbucket.org/anacrolix/go.torrent/util" + + "bitbucket.org/anacrolix/go.torrent" + "github.com/anacrolix/libtorgo/metainfo" + "github.com/go-fsnotify/fsnotify" ) type Change uint @@ -18,24 +23,38 @@ const ( ) type Event struct { - Magnet string + MagnetURI string Change TorrentFilePath string InfoHash torrent.InfoHash } +type entity struct { + torrent.InfoHash + MagnetURI string + TorrentFilePath string +} + type Instance struct { - w *fsnotify.Watcher - dirName string - Events chan Event - torrentFileInfoHashes map[string]torrent.InfoHash - magnetFileInfoHashes map[string]map[torrent.InfoHash]struct{} + w *fsnotify.Watcher + dirName string + Events chan Event + dirState map[torrent.InfoHash]entity +} + +func (me *Instance) Close() { + me.w.Close() } func (me *Instance) handleEvents() { + defer close(me.Events) for e := range me.w.Events { log.Printf("event: %s", e) - me.processFile(e.Name) + if e.Op == fsnotify.Write { + // TODO: Special treatment as an existing torrent may have changed. + } else { + me.refresh() + } } } @@ -50,13 +69,68 @@ func torrentFileInfoHash(fileName string) (ih torrent.InfoHash, ok bool) { if mi == nil { return } - if 20 != copy(ih[:], mi.Info.Hash) { - panic(mi.Info.Hash) - } + util.CopyExact(ih[:], mi.Info.Hash) ok = true return } +func scanDir(dirName string) (ee map[torrent.InfoHash]entity) { + d, err := os.Open(dirName) + if err != nil { + log.Print(err) + return + } + defer d.Close() + names, err := d.Readdirnames(-1) + if err != nil { + log.Print(err) + return + } + ee = make(map[torrent.InfoHash]entity, len(names)) + addEntity := func(e entity) { + e0, ok := ee[e.InfoHash] + if ok { + if e0.MagnetURI != "" && len(e.MagnetURI) < len(e0.MagnetURI) { + return + } + } + ee[e.InfoHash] = e + } + for _, n := range names { + fullName := filepath.Join(dirName, n) + switch filepath.Ext(n) { + case ".torrent": + ih, ok := torrentFileInfoHash(fullName) + if !ok { + break + } + e := entity{ + TorrentFilePath: fullName, + } + util.CopyExact(e.InfoHash, ih) + addEntity(e) + case ".magnet": + uris, err := magnetFileURIs(fullName) + if err != nil { + log.Print(err) + break + } + for _, uri := range uris { + m, err := torrent.ParseMagnetURI(uri) + if err != nil { + log.Print(err) + continue + } + addEntity(entity{ + InfoHash: m.InfoHash, + MagnetURI: uri, + }) + } + } + } + return +} + func magnetFileURIs(name string) (uris []string, err error) { f, err := os.Open(name) if err != nil { @@ -71,83 +145,42 @@ func magnetFileURIs(name string) (uris []string, err error) { return } -func (me *Instance) removeAllFileMagnets(name string) { - for ih := range me.magnetFileInfoHashes[name] { - me.Events <- Event{ - InfoHash: ih, - Change: Removed, - } +func (me *Instance) torrentRemoved(ih torrent.InfoHash) { + me.Events <- Event{ + InfoHash: ih, + Change: Removed, } } -func (me *Instance) removeTorrent(ih torrent.InfoHash) { +func (me *Instance) torrentAdded(e entity) { me.Events <- Event{ - InfoHash: ih, - Change: Removed, + InfoHash: e.InfoHash, + Change: Added, + MagnetURI: e.MagnetURI, + TorrentFilePath: e.TorrentFilePath, } } -func (me *Instance) processFile(name string) { - name = filepath.Clean(name) - log.Print(name) - switch filepath.Ext(name) { - case ".torrent": - ih, ok := me.torrentFileInfoHashes[name] - if ok { - me.Events <- Event{ - TorrentFilePath: name, - Change: Removed, - InfoHash: ih, - } +func (me *Instance) refresh() { + _new := scanDir(me.dirName) + old := me.dirState + for ih, _ := range old { + _, ok := _new[ih] + if !ok { + me.torrentRemoved(ih) } - delete(me.torrentFileInfoHashes, name) - ih, ok = torrentFileInfoHash(name) + } + for ih, newE := range _new { + oldE, ok := old[ih] if ok { - me.torrentFileInfoHashes[name] = ih - me.Events <- Event{ - TorrentFilePath: name, - Change: Added, - InfoHash: ih, - } - } - case ".magnet": - me.removeAllFileMagnets(name) - uris, err := magnetFileURIs(name) - if err != nil { - log.Print(err) - break - } - for _, uri := range uris { - m, err := torrent.ParseMagnetURI(uri) - if err != nil { - log.Printf("bad magnet uri in magnet file: %s", err) + if newE == oldE { continue } - me.removeTorrent(m.InfoHash) - me.Events <- Event{ - Magnet: uri, - Change: Added, - } + me.torrentRemoved(ih) } - default: - return - } -} - -func (me *Instance) addDir() (err error) { - f, err := os.Open(me.dirName) - if err != nil { - return + me.torrentAdded(newE) } - defer f.Close() - names, err := f.Readdirnames(-1) - if err != nil { - return - } - for _, n := range names { - me.processFile(filepath.Join(me.dirName, n)) - } - return + me.dirState = _new } func New(dirName string) (i *Instance, err error) { @@ -161,13 +194,13 @@ func New(dirName string) (i *Instance, err error) { return } i = &Instance{ - w: w, - dirName: dirName, - Events: make(chan Event), - torrentFileInfoHashes: make(map[string]torrent.InfoHash, 20), + w: w, + dirName: dirName, + Events: make(chan Event), + dirState: make(map[torrent.InfoHash]entity, 0), } go func() { - i.addDir() + i.refresh() go i.handleEvents() go i.handleErrors() }() diff --git a/util/dirwatch/dirwatch_test.go b/util/dirwatch/dirwatch_test.go new file mode 100644 index 00000000..7a975702 --- /dev/null +++ b/util/dirwatch/dirwatch_test.go @@ -0,0 +1,20 @@ +package dirwatch + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestDirwatch(t *testing.T) { + tempDirName, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDirName) + dw, err := New(tempDirName) + if err != nil { + t.Fatal(err) + } + dw.Close() +}