1 // Package dirwatch provides filesystem-notification based tracking of torrent
2 // info files and magnet URIs in a directory.
12 "github.com/anacrolix/missinggo"
13 "github.com/go-fsnotify/fsnotify"
15 "github.com/anacrolix/torrent"
16 "github.com/anacrolix/torrent/metainfo"
29 TorrentFilePath string
30 InfoHash metainfo.Hash
36 TorrentFilePath string
39 type Instance struct {
43 dirState map[metainfo.Hash]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 metainfo.Hash, ok bool) {
69 mi, _ := metainfo.LoadFromFile(fileName)
73 missinggo.CopyExact(ih[:], mi.Info.Hash)
78 func scanDir(dirName string) (ee map[metainfo.Hash]entity) {
79 d, err := os.Open(dirName)
85 names, err := d.Readdirnames(-1)
90 ee = make(map[metainfo.Hash]entity, len(names))
91 addEntity := func(e entity) {
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 missinggo.CopyExact(&e.Hash, 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)
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 metainfo.Hash) {
161 func (me *Instance) torrentAdded(e entity) {
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[metainfo.Hash]entity, 0),