// Downloads torrents from the command-line. package main import ( "context" "encoding/json" "fmt" "io" stdLog "log" "net/http" "os" "time" "github.com/anacrolix/bargle" "github.com/anacrolix/envpprof" "github.com/anacrolix/log" xprometheus "github.com/anacrolix/missinggo/v2/prometheus" "github.com/davecgh/go-spew/spew" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/sdk/trace" "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/version" ) func init() { prometheus.MustRegister(xprometheus.NewExpvarCollector()) http.Handle("/metrics", promhttp.Handler()) } func shutdownTracerProvider(ctx context.Context, tp *trace.TracerProvider) { started := time.Now() err := tp.Shutdown(ctx) elapsed := time.Since(started) log.Levelf(log.Error, "shutting down tracer provider (took %v): %v", elapsed, err) } func main() { defer stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile) ctx := context.Background() tracingExporter, err := otlptracegrpc.New(ctx) if err != nil { log.Fatalf("creating tracing exporter: %v", err) } tracerProvider := trace.NewTracerProvider(trace.WithBatcher(tracingExporter)) defer shutdownTracerProvider(ctx, tracerProvider) otel.SetTracerProvider(tracerProvider) main := bargle.Main{} main.Defer(envpprof.Stop) main.Defer(func() { shutdownTracerProvider(ctx, tracerProvider) }) 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 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 cmd }()}, bargle.Subcommand{Name: "download", Command: func() bargle.Command { var dlc DownloadCmd cmd := bargle.FromStruct(&dlc) cmd.DefaultAction = func() error { return downloadErr(downloadFlags{ Debug: debug, DownloadCmd: dlc, }) } return cmd }()}, bargle.Subcommand{ Name: "bencode", Command: func() (cmd bargle.Command) { var print func(interface{}) error 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 }()}, 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) 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) } 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 }, Desc: "prints various protocol default version strings", }}, bargle.Subcommand{Name: "serve", Command: serve()}, bargle.Subcommand{Name: "create", Command: create()}, ) main.Run() }