1 // Package dirwatch provides filesystem-notification based tracking of torrent
2 // info files and magnet URIs in a directory.
12 "bitbucket.org/anacrolix/go.torrent/util"
14 "bitbucket.org/anacrolix/go.torrent"
15 "github.com/anacrolix/libtorgo/metainfo"
16 "github.com/go-fsnotify/fsnotify"
29 TorrentFilePath string
30 InfoHash torrent.InfoHash
36 TorrentFilePath string
39 type Instance struct {
43 dirState map[torrent.InfoHash]entity
46 func (me *Instance) Close() {
50 func (me *Instance) handleEvents() {
51 defer close(me.Events)
52 for e := range me.w.Events {
53 log.Printf("event: %s", e)
54 if e.Op == fsnotify.Write {
55 // TODO: Special treatment as an existing torrent may have changed.
62 func (me *Instance) handleErrors() {
63 for err := range me.w.Errors {
64 log.Printf("error in torrent directory watcher: %s", err)
68 func torrentFileInfoHash(fileName string) (ih torrent.InfoHash, ok bool) {
69 mi, _ := metainfo.LoadFromFile(fileName)
73 util.CopyExact(ih[:], mi.Info.Hash)
78 func scanDir(dirName string) (ee map[torrent.InfoHash]entity) {
79 d, err := os.Open(dirName)
85 names, err := d.Readdirnames(-1)
90 ee = make(map[torrent.InfoHash]entity, len(names))
91 addEntity := func(e entity) {
92 e0, ok := ee[e.InfoHash]
94 if e0.MagnetURI == "" || len(e.MagnetURI) < len(e0.MagnetURI) {
100 for _, n := range names {
101 fullName := filepath.Join(dirName, n)
102 switch filepath.Ext(n) {
104 ih, ok := torrentFileInfoHash(fullName)
109 TorrentFilePath: fullName,
111 util.CopyExact(&e.InfoHash, ih)
114 uris, err := magnetFileURIs(fullName)
119 for _, uri := range uris {
120 m, err := torrent.ParseMagnetURI(uri)
122 log.Printf("error parsing %q in file %q: %s", uri, fullName, err)
126 InfoHash: m.InfoHash,
135 func magnetFileURIs(name string) (uris []string, err error) {
136 f, err := os.Open(name)
141 scanner := bufio.NewScanner(f)
142 scanner.Split(bufio.ScanWords)
144 // Allow magnet URIs to be "commented" out.
145 if strings.HasPrefix(scanner.Text(), "#") {
148 uris = append(uris, scanner.Text())
154 func (me *Instance) torrentRemoved(ih torrent.InfoHash) {
161 func (me *Instance) torrentAdded(e entity) {
163 InfoHash: e.InfoHash,
165 MagnetURI: e.MagnetURI,
166 TorrentFilePath: e.TorrentFilePath,
170 func (me *Instance) refresh() {
171 _new := scanDir(me.dirName)
173 for ih, _ := range old {
176 me.torrentRemoved(ih)
179 for ih, newE := range _new {
185 me.torrentRemoved(ih)
187 me.torrentAdded(newE)
192 func New(dirName string) (i *Instance, err error) {
193 w, err := fsnotify.NewWatcher()
205 Events: make(chan Event),
206 dirState: make(map[torrent.InfoHash]entity, 0),