]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Simplify dirwatch to scan the whole directory on changes
authorMatt Joiner <anacrolix@gmail.com>
Fri, 22 Aug 2014 07:31:03 +0000 (17:31 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Fri, 22 Aug 2014 07:31:03 +0000 (17:31 +1000)
cmd/torrentfs/main.go
util/dirwatch/dirwatch.go
util/dirwatch/dirwatch_test.go [new file with mode: 0644]

index 63ec59dfbbe939213da4a2eabca8c0f224c9301e..1b3d9797639aaab0875fa192c9533a3d9c0dedff 100644 (file)
@@ -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)
                                        }
index 05458217ee0d9b656a06d3fccdb0eeed4cb33e51..afc7653d76c66c99aba70459d9d7f2b697826e20 100644 (file)
@@ -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 (file)
index 0000000..7a97570
--- /dev/null
@@ -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()
+}