github.com/anacrolix/torrent@v1.61.0/cmd/torrent/main.go (about)

     1  // Downloads torrents from the command-line.
     2  package main
     3  
     4  import (
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	stdLog "log"
    10  	"log/slog"
    11  	"net/http"
    12  	"os"
    13  
    14  	"github.com/anacrolix/bargle"
    15  	"github.com/anacrolix/envpprof"
    16  	app "github.com/anacrolix/gostdapp"
    17  	"github.com/anacrolix/log"
    18  	xprometheus "github.com/anacrolix/missinggo/v2/prometheus"
    19  
    20  	"github.com/davecgh/go-spew/spew"
    21  	"github.com/prometheus/client_golang/prometheus"
    22  	"github.com/prometheus/client_golang/prometheus/promhttp"
    23  
    24  	"github.com/anacrolix/torrent/bencode"
    25  	"github.com/anacrolix/torrent/version"
    26  )
    27  
    28  func init() {
    29  	stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile)
    30  	prometheus.MustRegister(xprometheus.NewExpvarCollector())
    31  	http.Handle("/metrics", promhttp.Handler())
    32  	log.Default = log.NewLogger().WithDefaultLevel(log.Info)
    33  	log.Default.SetHandlers(log.SlogHandlerAsHandler{slog.Default().Handler()})
    34  }
    35  
    36  func main() {
    37  	app.RunContext(mainErr)
    38  }
    39  
    40  func mainErr(ctx context.Context) error {
    41  	main := bargle.Main{}
    42  	main.Defer(envpprof.Stop)
    43  	debug := false
    44  	debugFlag := bargle.NewFlag(&debug)
    45  	debugFlag.AddLong("debug")
    46  	main.Options = append(main.Options, debugFlag.Make())
    47  	main.AfterParseFunc = func(ctx bargle.Context) error {
    48  		if debug {
    49  			slog.SetLogLoggerLevel(slog.LevelDebug)
    50  		}
    51  		return nil
    52  	}
    53  	main.Positionals = append(main.Positionals,
    54  		bargle.Subcommand{Name: "metainfo", Command: metainfoCmd()},
    55  		bargle.Subcommand{Name: "announce", Command: func() bargle.Command {
    56  			var ac AnnounceCmd
    57  			cmd := bargle.FromStruct(&ac)
    58  			cmd.DefaultAction = func() error {
    59  				return announceErr(ac)
    60  			}
    61  			return cmd
    62  		}()},
    63  		bargle.Subcommand{Name: "scrape", Command: func() bargle.Command {
    64  			var scrapeCfg scrapeCfg
    65  			cmd := bargle.FromStruct(&scrapeCfg)
    66  			cmd.Desc = "fetch swarm metrics for info-hashes from tracker"
    67  			cmd.DefaultAction = func() error {
    68  				return scrape(scrapeCfg)
    69  			}
    70  			return cmd
    71  		}()},
    72  		bargle.Subcommand{Name: "download", Command: func() bargle.Command {
    73  			var dlc DownloadCmd
    74  			cmd := bargle.FromStruct(&dlc)
    75  			cmd.DefaultAction = func() error {
    76  				return downloadErr(
    77  					ctx,
    78  					downloadFlags{
    79  						Debug:       debug,
    80  						DownloadCmd: dlc,
    81  					},
    82  					slog.Default(),
    83  				)
    84  			}
    85  			return cmd
    86  		}()},
    87  		bargle.Subcommand{
    88  			Name: "bencode",
    89  			Command: func() (cmd bargle.Command) {
    90  				var print func(interface{}) error
    91  				cmd.Positionals = append(cmd.Positionals,
    92  					bargle.Subcommand{Name: "json", Command: func() (cmd bargle.Command) {
    93  						cmd.DefaultAction = func() error {
    94  							je := json.NewEncoder(os.Stdout)
    95  							je.SetIndent("", "  ")
    96  							print = je.Encode
    97  							return nil
    98  						}
    99  						return
   100  					}()},
   101  					bargle.Subcommand{Name: "spew", Command: func() (cmd bargle.Command) {
   102  						cmd.DefaultAction = func() error {
   103  							config := spew.NewDefaultConfig()
   104  							config.DisableCapacities = true
   105  							config.Indent = "  "
   106  							print = func(v interface{}) error {
   107  								config.Dump(v)
   108  								return nil
   109  							}
   110  							return nil
   111  						}
   112  						return
   113  					}()})
   114  				d := bencode.NewDecoder(os.Stdin)
   115  				cmd.AfterParseFunc = func(ctx bargle.Context) error {
   116  					ctx.AfterParse(func() error {
   117  						for i := 0; ; i++ {
   118  							var v interface{}
   119  							err := d.Decode(&v)
   120  							if err == io.EOF {
   121  								break
   122  							}
   123  							if err != nil {
   124  								return fmt.Errorf("decoding message index %d: %w", i, err)
   125  							}
   126  							print(v)
   127  						}
   128  						return nil
   129  					})
   130  					return nil
   131  				}
   132  				cmd.Desc = "reads bencoding from stdin into Go native types and spews the result"
   133  				return
   134  			}(),
   135  		},
   136  		bargle.Subcommand{Name: "version", Command: bargle.Command{
   137  			DefaultAction: func() error {
   138  				fmt.Printf("HTTP User-Agent: %q\n", version.DefaultHttpUserAgent)
   139  				fmt.Printf("Torrent client version: %q\n", version.DefaultExtendedHandshakeClientVersion)
   140  				fmt.Printf("Torrent version prefix: %q\n", version.DefaultBep20Prefix)
   141  				return nil
   142  			},
   143  			Desc: "prints various protocol default version strings",
   144  		}},
   145  		bargle.Subcommand{Name: "serve", Command: serve()},
   146  		bargle.Subcommand{Name: "create", Command: create()},
   147  	)
   148  	// Well this sux, this old version of bargle doesn't return so we can let the gostdapp Context
   149  	// clean up.
   150  	main.Run()
   151  	return nil
   152  }