cmd/torrent-create/main.go | 67 ----------------------------------------------------- cmd/torrent/announce.go | 4 ++-- cmd/torrent/create.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/torrent/main.go | 190 ++++++++++++++++++++++++++--------------------------- cmd/torrent/metainfo.go | 93 ++++++++++++++++++++++++++++++----------------------- cmd/torrent/scrape.go | 8 ++++---- cmd/torrent/serve.go | 117 ++++++++++++++++++++++++++++------------------------- go.mod | 1 + go.sum | 8 ++++++++ diff --git a/cmd/torrent-create/main.go b/cmd/torrent-create/main.go deleted file mode 100644 index 2f92978f0af2e206075a2a302c5767644676d295..0000000000000000000000000000000000000000 --- a/cmd/torrent-create/main.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "log" - "os" - - "github.com/anacrolix/tagflag" - - "github.com/anacrolix/torrent/bencode" - "github.com/anacrolix/torrent/metainfo" -) - -var builtinAnnounceList = [][]string{ - {"http://p4p.arenabg.com:1337/announce"}, - {"udp://tracker.opentrackr.org:1337/announce"}, - {"udp://tracker.openbittorrent.com:6969/announce"}, -} - -func main() { - log.SetFlags(log.Flags() | log.Lshortfile) - var args struct { - AnnounceList []string `name:"a" help:"extra announce-list tier entry"` - EmptyAnnounceList bool `name:"n" help:"exclude default announce-list entries"` - Comment string `name:"t" help:"comment"` - CreatedBy string `name:"c" help:"created by"` - InfoName *string `name:"i" help:"override info name (defaults to ROOT)"` - Url []string `name:"u" help:"add webseed url"` - tagflag.StartPos - Root string - } - tagflag.Parse(&args, tagflag.Description("Creates a torrent metainfo for the file system rooted at ROOT, and outputs it to stdout.")) - mi := metainfo.MetaInfo{ - AnnounceList: builtinAnnounceList, - } - if args.EmptyAnnounceList { - mi.AnnounceList = make([][]string, 0) - } - for _, a := range args.AnnounceList { - mi.AnnounceList = append(mi.AnnounceList, []string{a}) - } - mi.SetDefaults() - if len(args.Comment) > 0 { - mi.Comment = args.Comment - } - if len(args.CreatedBy) > 0 { - mi.CreatedBy = args.CreatedBy - } - mi.UrlList = args.Url - info := metainfo.Info{ - PieceLength: 256 * 1024, - } - err := info.BuildFromFilePath(args.Root) - if err != nil { - log.Fatal(err) - } - if args.InfoName != nil { - info.Name = *args.InfoName - } - mi.InfoBytes, err = bencode.Marshal(info) - if err != nil { - log.Fatal(err) - } - err = mi.Write(os.Stdout) - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/torrent/announce.go b/cmd/torrent/announce.go index 277f3bef52ff6feb6519c8672fbe8e1c9bbfb086..b40120f112dddc1e9ecb6453e44b6df445b4bb52 100644 --- a/cmd/torrent/announce.go +++ b/cmd/torrent/announce.go @@ -10,8 +10,8 @@ "github.com/anacrolix/torrent/tracker" ) type AnnounceCmd struct { - Tracker string `arg:"positional"` - InfoHash torrent.InfoHash + Tracker string `arg:"positional"` + InfoHash torrent.InfoHash `arg:"positional"` } func announceErr(flags AnnounceCmd) error { diff --git a/cmd/torrent/create.go b/cmd/torrent/create.go new file mode 100644 index 0000000000000000000000000000000000000000..8f4440c44c15e96391c030bf3a75590a4b086c27 --- /dev/null +++ b/cmd/torrent/create.go @@ -0,0 +1,68 @@ +package main + +import ( + "github.com/anacrolix/bargle" + "github.com/anacrolix/tagflag" + "github.com/anacrolix/torrent/bencode" + "github.com/anacrolix/torrent/metainfo" + "os" +) + +var builtinAnnounceList = [][]string{ + {"http://p4p.arenabg.com:1337/announce"}, + {"udp://tracker.opentrackr.org:1337/announce"}, + {"udp://tracker.openbittorrent.com:6969/announce"}, +} + +func create() (cmd bargle.Command) { + var args struct { + AnnounceList []string `name:"a" help:"extra announce-list tier entry"` + EmptyAnnounceList bool `name:"n" help:"exclude default announce-list entries"` + Comment string `name:"t" help:"comment"` + CreatedBy string `name:"c" help:"created by"` + InfoName *string `name:"i" help:"override info name (defaults to ROOT)"` + PieceLength tagflag.Bytes + Url []string `name:"u" help:"add webseed url"` + Private *bool + Root string `arg:"positional"` + } + cmd = bargle.FromStruct(&args) + cmd.Desc = "Creates a torrent metainfo for the file system rooted at ROOT, and outputs it to stdout" + cmd.DefaultAction = func() (err error) { + mi := metainfo.MetaInfo{ + AnnounceList: builtinAnnounceList, + } + if args.EmptyAnnounceList { + mi.AnnounceList = make([][]string, 0) + } + for _, a := range args.AnnounceList { + mi.AnnounceList = append(mi.AnnounceList, []string{a}) + } + mi.SetDefaults() + if len(args.Comment) > 0 { + mi.Comment = args.Comment + } + if len(args.CreatedBy) > 0 { + mi.CreatedBy = args.CreatedBy + } + mi.UrlList = args.Url + info := metainfo.Info{ + PieceLength: args.PieceLength.Int64(), + Private: args.Private, + } + err = info.BuildFromFilePath(args.Root) + if err != nil { + return + } + if args.InfoName != nil { + info.Name = *args.InfoName + } + mi.InfoBytes, err = bencode.Marshal(info) + if err != nil { + return + } + err = mi.Write(os.Stdout) + return + } + return +} diff --git a/cmd/torrent/main.go b/cmd/torrent/main.go index 34219b513ab60b56a69b8b08f4e2358a726f39b8..8c942e391af6cdec1c3ce6059d98aa91356f14ea 100644 --- a/cmd/torrent/main.go +++ b/cmd/torrent/main.go @@ -3,131 +3,127 @@ package main import ( "encoding/json" - "errors" "fmt" "io" stdLog "log" "net/http" "os" - "github.com/anacrolix/args" + "github.com/davecgh/go-spew/spew" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/anacrolix/bargle" "github.com/anacrolix/envpprof" - "github.com/anacrolix/log" xprometheus "github.com/anacrolix/missinggo/v2/prometheus" + "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/version" - "github.com/davecgh/go-spew/spew" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" ) -func main() { - if err := mainErr(); err != nil { - log.Printf("error in main: %v", err) - os.Exit(1) - } -} - func init() { prometheus.MustRegister(xprometheus.NewExpvarCollector()) http.Handle("/metrics", promhttp.Handler()) } -func mainErr() error { - defer envpprof.Stop() - stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile) - debug := args.Flag(args.FlagOpt{Long: "debug"}) - return args.ParseMain( - debug, - args.Subcommand("metainfo", metainfoCmd), - args.Subcommand("announce", func(p args.SubCmdCtx) error { - var cmd AnnounceCmd - err := p.NewParser().AddParams( - args.Pos("tracker", &cmd.Tracker), - args.Pos("infohash", &cmd.InfoHash)).Parse() - if err != nil { - return err +func main() { + defer stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile) + main := bargle.Main{} + main.Defer(envpprof.Stop) + debug := false + debugFlag := bargle.NewFlag(&debug) + debugFlag.AddLong("debug") + main.Options = append(main.Options, debugFlag.Make()) + main.Positionals = append(main.Positionals, + bargle.Subcommand{Name: "metainfo", Command: metainfoCmd()}, + bargle.Subcommand{Name: "announce", Command: func() bargle.Command { + var ac AnnounceCmd + cmd := bargle.FromStruct(&ac) + cmd.DefaultAction = func() error { + return announceErr(ac) } - return announceErr(cmd) - }), - args.Subcommand("scrape", func(p args.SubCmdCtx) error { - var cmd ScrapeCmd - err := p.NewParser().AddParams( - args.Pos("tracker", &cmd.Tracker), - args.Pos("infohash", &cmd.InfoHashes, args.Arity('+'))).Parse() - if err != nil { - return err + return cmd + }()}, + bargle.Subcommand{Name: "scrape", Command: func() bargle.Command { + var scrapeCfg scrapeCfg + cmd := bargle.FromStruct(&scrapeCfg) + cmd.Desc = "fetch swarm metrics for info-hashes from tracker" + cmd.DefaultAction = func() error { + return scrape(scrapeCfg) } - return scrape(cmd) - }), - args.Subcommand("download", func(p args.SubCmdCtx) error { + return cmd + }()}, + bargle.Subcommand{Name: "download", Command: func() bargle.Command { var dlc DownloadCmd - err := p.NewParser().AddParams( - append(args.FromStruct(&dlc), debug)..., - ).Parse() - if err != nil { - return err + cmd := bargle.FromStruct(&dlc) + cmd.DefaultAction = func() error { + return downloadErr(downloadFlags{ + Debug: debug, + DownloadCmd: dlc, + }) } - dlf := downloadFlags{ - Debug: debug.Bool(), - DownloadCmd: dlc, - } - p.Defer(func() error { - return downloadErr(dlf) - }) - return nil - }), - args.Subcommand( - "bencode", - func(p args.SubCmdCtx) error { + return cmd + }()}, + bargle.Subcommand{ + Name: "bencode", + Command: func() (cmd bargle.Command) { var print func(interface{}) error - if !p.Parse( - args.Subcommand("json", func(ctx args.SubCmdCtx) (err error) { - ctx.Parse() - je := json.NewEncoder(os.Stdout) - je.SetIndent("", " ") - print = je.Encode - return nil - }), - args.Subcommand("spew", func(ctx args.SubCmdCtx) (err error) { - ctx.Parse() - config := spew.NewDefaultConfig() - config.DisableCapacities = true - config.Indent = " " - print = func(v interface{}) error { - config.Dump(v) + cmd.Positionals = append(cmd.Positionals, + bargle.Subcommand{Name: "json", Command: func() (cmd bargle.Command) { + cmd.DefaultAction = func() error { + je := json.NewEncoder(os.Stdout) + je.SetIndent("", " ") + print = je.Encode return nil } - return nil - }), - ).RanSubCmd { - return errors.New("an output type is required") - } + return + }()}, + bargle.Subcommand{Name: "spew", Command: func() (cmd bargle.Command) { + cmd.DefaultAction = func() error { + config := spew.NewDefaultConfig() + config.DisableCapacities = true + config.Indent = " " + print = func(v interface{}) error { + config.Dump(v) + return nil + } + return nil + } + return + }()}) d := bencode.NewDecoder(os.Stdin) - p.Defer(func() error { - for i := 0; ; i++ { - var v interface{} - err := d.Decode(&v) - if err == io.EOF { - break - } - if err != nil { - return fmt.Errorf("decoding message index %d: %w", i, err) + cmd.AfterParseFunc = func(ctx bargle.Context) error { + ctx.AfterParse(func() error { + for i := 0; ; i++ { + var v interface{} + err := d.Decode(&v) + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("decoding message index %d: %w", i, err) + } + print(v) } - print(v) - } + return nil + }) return nil - }) + } + cmd.Desc = "reads bencoding from stdin into Go native types and spews the result" + return + }(), + }, + bargle.Subcommand{Name: "version", Command: bargle.Command{ + DefaultAction: func() error { + fmt.Printf("HTTP User-Agent: %q\n", version.DefaultHttpUserAgent) + fmt.Printf("Torrent client version: %q\n", version.DefaultExtendedHandshakeClientVersion) + fmt.Printf("Torrent version prefix: %q\n", version.DefaultBep20Prefix) return nil }, - args.Help("reads bencoding from stdin into Go native types and spews the result"), - ), - args.Subcommand("version", func(p args.SubCmdCtx) error { - fmt.Printf("HTTP User-Agent: %q\n", version.DefaultHttpUserAgent) - fmt.Printf("Torrent client version: %q\n", version.DefaultExtendedHandshakeClientVersion) - fmt.Printf("Torrent version prefix: %q\n", version.DefaultBep20Prefix) - return nil - }), - args.Subcommand("serve", serve, args.Help("creates and seeds a torrent from a filepath")), + Desc: "prints various protocol default version strings", + }}, + bargle.Subcommand{Name: "serve", Command: serve()}, + bargle.Subcommand{Name: "create", Command: create()}, ) + main.Run() } diff --git a/cmd/torrent/metainfo.go b/cmd/torrent/metainfo.go index f8f558ff332aaf76971762b9a3b28ca46a774666..c3e37e5b99e834791427a2785819660f628e573c 100644 --- a/cmd/torrent/metainfo.go +++ b/cmd/torrent/metainfo.go @@ -6,7 +6,7 @@ "encoding/json" "fmt" "os" - "github.com/anacrolix/args" + "github.com/anacrolix/bargle" "github.com/anacrolix/torrent/metainfo" "github.com/bradfitz/iter" ) @@ -17,53 +17,64 @@ PieceHashes bool Files bool } -func metainfoCmd(ctx args.SubCmdCtx) (err error) { +func metainfoCmd() (cmd bargle.Command) { var metainfoPath string var mi *metainfo.MetaInfo - // TODO: Treat no subcommand as a failure. - return ctx.NewParser().AddParams( - args.Pos("torrent file", &metainfoPath, args.AfterParse(func() (err error) { - mi, err = metainfo.LoadFromFile(metainfoPath) + // TODO: Test if bargle treats no subcommand as a failure. + cmd.Positionals = append(cmd.Positionals, + &bargle.Positional{ + Name: "torrent file", + Value: &bargle.String{Target: &metainfoPath}, + AfterParseFunc: func(ctx bargle.Context) error { + ctx.AfterParse(func() (err error) { + mi, err = metainfo.LoadFromFile(metainfoPath) + return + }) + return nil + }, + }, + bargle.Subcommand{Name: "magnet", Command: func() (cmd bargle.Command) { + cmd.DefaultAction = func() (err error) { + info, err := mi.UnmarshalInfo() + if err != nil { + return + } + fmt.Fprintf(os.Stdout, "%s\n", mi.Magnet(nil, &info).String()) + return nil + } return - })), - args.Subcommand("magnet", func(ctx args.SubCmdCtx) (err error) { - info, err := mi.UnmarshalInfo() - if err != nil { - return - } - fmt.Fprintf(os.Stdout, "%s\n", mi.Magnet(nil, &info).String()) - return nil - }), - args.Subcommand("pprint", func(ctx args.SubCmdCtx) (err error) { + }()}, + bargle.Subcommand{Name: "pprint", Command: func() (cmd bargle.Command) { var flags pprintMetainfoFlags - err = ctx.NewParser().AddParams(args.FromStruct(&flags)...).Parse() - if err != nil { - return - } - err = pprintMetainfo(mi, flags) - if err != nil { + cmd = bargle.FromStruct(&flags) + cmd.DefaultAction = func() (err error) { + err = pprintMetainfo(mi, flags) + if err != nil { + return + } + if !flags.JustName { + os.Stdout.WriteString("\n") + } return } - if !flags.JustName { - os.Stdout.WriteString("\n") - } return - }), - args.Subcommand("infohash", func(ctx args.SubCmdCtx) (err error) { - fmt.Printf("%s: %s\n", mi.HashInfoBytes().HexString(), metainfoPath) - return nil - }), - args.Subcommand("list-files", func(ctx args.SubCmdCtx) (err error) { - info, err := mi.UnmarshalInfo() - if err != nil { - return fmt.Errorf("unmarshalling info from metainfo at %q: %v", metainfoPath, err) - } - for _, f := range info.UpvertedFiles() { - fmt.Println(f.DisplayPath(&info)) - } - return nil - }), - ).Parse() + }()}, + //bargle.Subcommand{Name: "infohash", Command: func(ctx args.SubCmdCtx) (err error) { + // fmt.Printf("%s: %s\n", mi.HashInfoBytes().HexString(), metainfoPath) + // return nil + //}}, + //bargle.Subcommand{Name: "list-files", Command: func(ctx args.SubCmdCtx) (err error) { + // info, err := mi.UnmarshalInfo() + // if err != nil { + // return fmt.Errorf("unmarshalling info from metainfo at %q: %v", metainfoPath, err) + // } + // for _, f := range info.UpvertedFiles() { + // fmt.Println(f.DisplayPath(&info)) + // } + // return nil + //}}, + ) + return } func pprintMetainfo(metainfo *metainfo.MetaInfo, flags pprintMetainfoFlags) error { diff --git a/cmd/torrent/scrape.go b/cmd/torrent/scrape.go index a0a93b5755a9ca1efbd75611898ceaa2877e4830..56b9c70cb8a8e46f14cf6b43222c0f61c899c6a0 100644 --- a/cmd/torrent/scrape.go +++ b/cmd/torrent/scrape.go @@ -11,12 +11,12 @@ "github.com/anacrolix/torrent" ) -type ScrapeCmd struct { - Tracker string `arg:"positional"` - InfoHashes []torrent.InfoHash +type scrapeCfg struct { + Tracker string `arg:"positional"` + InfoHashes []torrent.InfoHash `arity:"+" arg:"positional"` } -func scrape(flags ScrapeCmd) error { +func scrape(flags scrapeCfg) error { trackerUrl, err := url.Parse(flags.Tracker) if err != nil { return fmt.Errorf("parsing tracker url: %w", err) diff --git a/cmd/torrent/serve.go b/cmd/torrent/serve.go index b32f245b089dc83813e7d5de8c56ba7e235eb0c9..0d3487d4355f407a4827802841a2a030e54abae2 100644 --- a/cmd/torrent/serve.go +++ b/cmd/torrent/serve.go @@ -5,7 +5,7 @@ "fmt" "net/http" "path/filepath" - "github.com/anacrolix/args" + "github.com/anacrolix/bargle" "github.com/anacrolix/log" "github.com/anacrolix/torrent" "github.com/anacrolix/torrent/bencode" @@ -13,10 +13,13 @@ "github.com/anacrolix/torrent/metainfo" "github.com/anacrolix/torrent/storage" ) -func serve(ctx args.SubCmdCtx) error { - var filePath string - ctx.Parse(args.Pos("filePath", &filePath)) - ctx.Defer(func() error { +func serve() (cmd bargle.Command) { + var filePaths []string + cmd.Positionals = append(cmd.Positionals, &bargle.Positional{ + Value: bargle.AutoUnmarshaler(&filePaths), + }) + cmd.Desc = "creates and seeds a torrent from a filepath" + cmd.DefaultAction = func() error { cfg := torrent.NewDefaultClientConfig() cfg.Seed = true cl, err := torrent.NewClient(cfg) @@ -27,57 +30,59 @@ defer cl.Close() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { cl.WriteStatus(w) }) - totalLength, err := totalLength(filePath) - if err != nil { - return fmt.Errorf("calculating total length of %q: %v", filePath, err) - } - pieceLength := metainfo.ChoosePieceLength(totalLength) - info := metainfo.Info{ - PieceLength: pieceLength, - } - err = info.BuildFromFilePath(filePath) - if err != nil { - return fmt.Errorf("building info from path %q: %w", filePath, err) - } - for _, fi := range info.Files { - log.Printf("added %q", fi.Path) - } - mi := metainfo.MetaInfo{ - InfoBytes: bencode.MustMarshal(info), - } - pc, err := storage.NewDefaultPieceCompletionForDir(".") - if err != nil { - return fmt.Errorf("new piece completion: %w", err) - } - defer pc.Close() - ih := mi.HashInfoBytes() - to, _ := cl.AddTorrentOpt(torrent.AddTorrentOpts{ - InfoHash: ih, - Storage: storage.NewFileOpts(storage.NewFileClientOpts{ - ClientBaseDir: filePath, - FilePathMaker: func(opts storage.FilePathMakerOpts) string { - return filepath.Join(opts.File.Path...) - }, - TorrentDirMaker: nil, - PieceCompletion: pc, - }), - }) - defer to.Drop() - err = to.MergeSpec(&torrent.TorrentSpec{ - InfoBytes: mi.InfoBytes, - Trackers: [][]string{{ - `wss://tracker.btorrent.xyz`, - `wss://tracker.openwebtorrent.com`, - "http://p4p.arenabg.com:1337/announce", - "udp://tracker.opentrackr.org:1337/announce", - "udp://tracker.openbittorrent.com:6969/announce", - }}, - }) - if err != nil { - return fmt.Errorf("setting trackers: %w", err) + for _, filePath := range filePaths { + totalLength, err := totalLength(filePath) + if err != nil { + return fmt.Errorf("calculating total length of %q: %v", filePath, err) + } + pieceLength := metainfo.ChoosePieceLength(totalLength) + info := metainfo.Info{ + PieceLength: pieceLength, + } + err = info.BuildFromFilePath(filePath) + if err != nil { + return fmt.Errorf("building info from path %q: %w", filePath, err) + } + for _, fi := range info.Files { + log.Printf("added %q", fi.Path) + } + mi := metainfo.MetaInfo{ + InfoBytes: bencode.MustMarshal(info), + } + pc, err := storage.NewDefaultPieceCompletionForDir(".") + if err != nil { + return fmt.Errorf("new piece completion: %w", err) + } + defer pc.Close() + ih := mi.HashInfoBytes() + to, _ := cl.AddTorrentOpt(torrent.AddTorrentOpts{ + InfoHash: ih, + Storage: storage.NewFileOpts(storage.NewFileClientOpts{ + ClientBaseDir: filePath, + FilePathMaker: func(opts storage.FilePathMakerOpts) string { + return filepath.Join(opts.File.Path...) + }, + TorrentDirMaker: nil, + PieceCompletion: pc, + }), + }) + defer to.Drop() + err = to.MergeSpec(&torrent.TorrentSpec{ + InfoBytes: mi.InfoBytes, + Trackers: [][]string{{ + `wss://tracker.btorrent.xyz`, + `wss://tracker.openwebtorrent.com`, + "http://p4p.arenabg.com:1337/announce", + "udp://tracker.opentrackr.org:1337/announce", + "udp://tracker.openbittorrent.com:6969/announce", + }}, + }) + if err != nil { + return fmt.Errorf("setting trackers: %w", err) + } + fmt.Printf("%v: %v\n", to, to.Metainfo().Magnet(&ih, &info)) } - fmt.Printf("%v: %v\n", to, to.Metainfo().Magnet(&ih, &info)) select {} - }) - return nil + } + return } diff --git a/go.mod b/go.mod index a3313d6bed31a92c3febcaccdae1e3d254975856..8e351f99c4ae461e6286125d6e9816bb5a9b6e87 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/alecthomas/atomic v0.1.0-alpha2 // indirect github.com/alexflint/go-scalar v1.1.0 // indirect + github.com/anacrolix/bargle v0.0.0-20220630015206-d7a4d433886a // indirect github.com/anacrolix/mmsg v1.0.0 // indirect github.com/anacrolix/stm v0.4.0 // indirect github.com/benbjohnson/immutable v0.3.0 // indirect diff --git a/go.sum b/go.sum index 97592e176889adffd5b8dcf76b3923a8b1bfe5a8..b936cbe34d1cc4de00469e23c1cb9140d18a32b9 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,14 @@ github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM= github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/anacrolix/args v0.5.1-0.20220509024600-c3b77d0b61ac h1:XWoepbk3zgOQ8jMO3vpOnohd6MfENPbFZPivB2L7myc= github.com/anacrolix/args v0.5.1-0.20220509024600-c3b77d0b61ac/go.mod h1:Fj/N2PehEwTBE5t/V/9xgTcxDkuYQ+5IBoFw/8gkldI= +github.com/anacrolix/bargle v0.0.0-20220620083758-c3885e1796d1 h1:RijTNFCxug0EBODz/AmqJDA3Ow700v5jJZQL1j+RLvI= +github.com/anacrolix/bargle v0.0.0-20220620083758-c3885e1796d1/go.mod h1:cC/kX8wL4i1n+63lOrXhPQQlsoxCo0EqV88fGExQwcY= +github.com/anacrolix/bargle v0.0.0-20220622082028-6c0bfc8b614d h1:eSBxjJUsa4p5lCn8mlp6gOdzhZYZxqr4YHPk8Uwn1iQ= +github.com/anacrolix/bargle v0.0.0-20220622082028-6c0bfc8b614d/go.mod h1:9xUiZbkh+94FbiIAL1HXpAIBa832f3Mp07rRPl5c5RQ= +github.com/anacrolix/bargle v0.0.0-20220627055849-08d7fa720ece h1:QPZd4ViWNy2uxIbrj1fIhvLxpe27GrnzaaVHGfY6Aac= +github.com/anacrolix/bargle v0.0.0-20220627055849-08d7fa720ece/go.mod h1:9xUiZbkh+94FbiIAL1HXpAIBa832f3Mp07rRPl5c5RQ= +github.com/anacrolix/bargle v0.0.0-20220630015206-d7a4d433886a h1:KCP9QvHlLoUQBOaTf/YCuOzG91Ym1cPB6S68O4Q3puo= +github.com/anacrolix/bargle v0.0.0-20220630015206-d7a4d433886a/go.mod h1:9xUiZbkh+94FbiIAL1HXpAIBa832f3Mp07rRPl5c5RQ= github.com/anacrolix/chansync v0.3.0 h1:lRu9tbeuw3wl+PhMu/r+JJCRu5ArFXIluOgdF0ao6/U= github.com/anacrolix/chansync v0.3.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= github.com/anacrolix/dht/v2 v2.18.0 h1:btjVjzjKqO5nKGbJHJ2UmuwiRx+EgX3e+OCHC9+WRz8=