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/metainfo"
28 TorrentFilePath string
29 InfoHash metainfo.Hash
35 TorrentFilePath string
38 type Instance struct {
42 dirState map[metainfo.Hash]entity
45 func (i *Instance) Close() {
49 func (i *Instance) handleEvents() {
51 for e := range i.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 (i *Instance) handleErrors() {
62 for err := range i.w.Errors {
63 log.Printf("error in torrent directory watcher: %s", err)
67 func torrentFileInfoHash(fileName string) (ih metainfo.Hash, ok bool) {
68 mi, _ := metainfo.LoadFromFile(fileName)
72 ih = mi.HashInfoBytes()
77 func scanDir(dirName string) (ee map[metainfo.Hash]entity) {
78 d, err := os.Open(dirName)
84 names, err := d.Readdirnames(-1)
89 ee = make(map[metainfo.Hash]entity, len(names))
90 addEntity := func(e entity) {
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 missinggo.CopyExact(&e.Hash, ih)
113 uris, err := magnetFileURIs(fullName)
118 for _, uri := range uris {
119 m, err := metainfo.ParseMagnetURI(uri)
121 log.Printf("error parsing %q in file %q: %s", uri, fullName, err)
134 func magnetFileURIs(name string) (uris []string, err error) {
135 f, err := os.Open(name)
140 scanner := bufio.NewScanner(f)
141 scanner.Split(bufio.ScanWords)
143 // Allow magnet URIs to be "commented" out.
144 if strings.HasPrefix(scanner.Text(), "#") {
147 uris = append(uris, scanner.Text())
153 func (i *Instance) torrentRemoved(ih metainfo.Hash) {
160 func (i *Instance) torrentAdded(e entity) {
164 MagnetURI: e.MagnetURI,
165 TorrentFilePath: e.TorrentFilePath,
169 func (i *Instance) refresh() {
170 _new := scanDir(i.dirName)
172 for ih, _ := range old {
178 for ih, newE := range _new {
191 func New(dirName string) (i *Instance, err error) {
192 w, err := fsnotify.NewWatcher()
204 Events: make(chan Event),
205 dirState: make(map[metainfo.Hash]entity, 0),