1 // Package dirwatch provides filesystem-notification based tracking of torrent
2 // info files and magnet URIs in a directory.
11 "bitbucket.org/anacrolix/go.torrent/util"
13 "bitbucket.org/anacrolix/go.torrent"
14 "github.com/anacrolix/libtorgo/metainfo"
15 "github.com/go-fsnotify/fsnotify"
28 TorrentFilePath string
29 InfoHash torrent.InfoHash
35 TorrentFilePath string
38 type Instance struct {
42 dirState map[torrent.InfoHash]entity
45 func (me *Instance) Close() {
49 func (me *Instance) handleEvents() {
50 defer close(me.Events)
51 for e := range me.w.Events {
52 log.Printf("event: %s", e)
53 if e.Op == fsnotify.Write {
54 // TODO: Special treatment as an existing torrent may have changed.
61 func (me *Instance) handleErrors() {
62 for err := range me.w.Errors {
63 log.Printf("error in torrent directory watcher: %s", err)
67 func torrentFileInfoHash(fileName string) (ih torrent.InfoHash, ok bool) {
68 mi, _ := metainfo.LoadFromFile(fileName)
72 util.CopyExact(ih[:], mi.Info.Hash)
77 func scanDir(dirName string) (ee map[torrent.InfoHash]entity) {
78 d, err := os.Open(dirName)
84 names, err := d.Readdirnames(-1)
89 ee = make(map[torrent.InfoHash]entity, len(names))
90 addEntity := func(e entity) {
91 e0, ok := ee[e.InfoHash]
93 if e0.MagnetURI != "" && len(e.MagnetURI) < len(e0.MagnetURI) {
99 for _, n := range names {
100 fullName := filepath.Join(dirName, n)
101 switch filepath.Ext(n) {
103 ih, ok := torrentFileInfoHash(fullName)
108 TorrentFilePath: fullName,
110 util.CopyExact(e.InfoHash, ih)
113 uris, err := magnetFileURIs(fullName)
118 for _, uri := range uris {
119 m, err := torrent.ParseMagnetURI(uri)
125 InfoHash: m.InfoHash,
134 func magnetFileURIs(name string) (uris []string, err error) {
135 f, err := os.Open(name)
140 scanner := bufio.NewScanner(f)
142 uris = append(uris, scanner.Text())
148 func (me *Instance) torrentRemoved(ih torrent.InfoHash) {
155 func (me *Instance) torrentAdded(e entity) {
157 InfoHash: e.InfoHash,
159 MagnetURI: e.MagnetURI,
160 TorrentFilePath: e.TorrentFilePath,
164 func (me *Instance) refresh() {
165 _new := scanDir(me.dirName)
167 for ih, _ := range old {
170 me.torrentRemoved(ih)
173 for ih, newE := range _new {
179 me.torrentRemoved(ih)
181 me.torrentAdded(newE)
186 func New(dirName string) (i *Instance, err error) {
187 w, err := fsnotify.NewWatcher()
199 Events: make(chan Event),
200 dirState: make(map[torrent.InfoHash]entity, 0),