tdl/cmd/root.go

184 lines
5.9 KiB
Go
Raw Normal View History

2022-09-01 15:16:59 +08:00
package cmd
import (
"context"
2023-12-04 10:26:39 +08:00
"fmt"
2023-08-20 21:00:06 +08:00
"path/filepath"
"strings"
"time"
2023-12-04 10:26:39 +08:00
"github.com/go-faster/errors"
"github.com/gotd/td/telegram"
2022-09-01 15:16:59 +08:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
2023-12-04 10:26:39 +08:00
"go.uber.org/multierr"
2023-01-30 19:56:08 +08:00
"go.uber.org/zap"
2023-08-20 21:00:06 +08:00
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/kv"
"github.com/iyear/tdl/pkg/logger"
"github.com/iyear/tdl/pkg/tclient"
"github.com/iyear/tdl/pkg/utils"
)
var (
defaultBoltPath = filepath.Join(consts.DataDir, "data")
DefaultLegacyStorage = map[string]string{
kv.DriverTypeKey: kv.DriverLegacy.String(),
"path": filepath.Join(consts.DataDir, "data.kv"),
}
DefaultBoltStorage = map[string]string{
kv.DriverTypeKey: kv.DriverBolt.String(),
"path": defaultBoltPath,
}
2022-09-01 15:16:59 +08:00
)
2023-03-29 18:20:23 +08:00
func New() *cobra.Command {
cmd := &cobra.Command{
2023-10-02 19:33:19 +08:00
Use: "tdl",
Short: "Telegram Downloader, but more than a downloader",
SilenceErrors: true,
SilenceUsage: true,
2023-12-04 10:26:39 +08:00
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
2023-03-29 18:20:23 +08:00
// init logger
debug, level := viper.GetBool(consts.FlagDebug), zap.InfoLevel
if debug {
level = zap.DebugLevel
}
2023-03-29 18:24:41 +08:00
cmd.SetContext(logger.With(cmd.Context(),
logger.New(level, filepath.Join(consts.LogPath, "latest.log"))))
2022-10-19 10:42:03 +08:00
2023-03-29 18:20:23 +08:00
ns := viper.GetString(consts.FlagNamespace)
if ns != "" {
logger.From(cmd.Context()).Info("Namespace",
zap.String("namespace", ns))
}
2023-12-04 10:26:39 +08:00
// v0.14.0: default storage changed from legacy to bolt, so we need to auto migrate to keep compatibility
if !cmd.Flags().Lookup(consts.FlagStorage).Changed && !utils.FS.PathExists(defaultBoltPath) {
if err := migrateLegacyToBolt(); err != nil {
return errors.Wrap(err, "migrate legacy to bolt")
}
}
storage, err := kv.NewWithMap(viper.GetStringMapString(consts.FlagStorage))
2023-12-04 10:26:39 +08:00
if err != nil {
return errors.Wrap(err, "create kv storage")
}
cmd.SetContext(kv.With(cmd.Context(), storage))
return nil
2023-03-29 18:20:23 +08:00
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
2023-12-04 10:26:39 +08:00
return multierr.Combine(
kv.From(cmd.Context()).Close(),
logger.From(cmd.Context()).Sync(),
)
2023-03-29 18:20:23 +08:00
},
}
2023-11-16 23:04:07 +08:00
cmd.AddCommand(NewVersion(), NewLogin(), NewDownload(), NewForward(),
NewChat(), NewUpload(), NewBackup(), NewRecover(), NewMigrate(), NewGen())
2022-09-01 15:16:59 +08:00
cmd.PersistentFlags().StringToString(consts.FlagStorage,
DefaultBoltStorage,
fmt.Sprintf("storage options, format: type=driver,key1=value1,key2=value2. Available drivers: [%s]",
strings.Join(kv.DriverNames(), ",")))
2023-12-04 10:26:39 +08:00
2023-12-04 15:58:53 +08:00
cmd.PersistentFlags().String(consts.FlagProxy, "", "proxy address, format: protocol://username:password@host:port")
2023-12-17 20:11:21 +08:00
cmd.PersistentFlags().StringP(consts.FlagNamespace, "n", "default", "namespace for Telegram session")
2022-09-19 20:48:16 +08:00
cmd.PersistentFlags().Bool(consts.FlagDebug, false, "enable debug mode")
2022-09-01 15:16:59 +08:00
cmd.PersistentFlags().IntP(consts.FlagPartSize, "s", 512*1024, "part size for transfer, max is 512*1024")
cmd.PersistentFlags().IntP(consts.FlagThreads, "t", 4, "max threads for transfer one item")
cmd.PersistentFlags().IntP(consts.FlagLimit, "l", 2, "max number of concurrent tasks")
2023-11-25 11:53:51 +08:00
cmd.PersistentFlags().Int(consts.FlagPoolSize, 8, "specify the size of the DC pool, zero means infinity")
2022-09-21 20:52:43 +08:00
cmd.PersistentFlags().String(consts.FlagNTP, "", "ntp server host, if not set, use system time")
cmd.PersistentFlags().Duration(consts.FlagReconnectTimeout, 2*time.Minute, "Telegram client reconnection backoff timeout, infinite if set to 0") // #158
2022-09-21 20:52:43 +08:00
2023-05-26 23:45:00 +08:00
cmd.PersistentFlags().String(consts.FlagTest, "", "use test Telegram client, only for developer")
// completion
_ = cmd.RegisterFlagCompletionFunc(consts.FlagNamespace, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
2023-12-04 10:26:39 +08:00
engine := kv.From(cmd.Context())
ns, err := engine.Namespaces()
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return ns, cobra.ShellCompDirectiveNoFileComp
})
_ = viper.BindPFlags(cmd.PersistentFlags())
2022-09-19 20:48:16 +08:00
viper.SetEnvPrefix("tdl")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
2022-09-19 20:48:16 +08:00
viper.AutomaticEnv()
2022-09-21 20:52:43 +08:00
2023-03-29 18:20:23 +08:00
return cmd
}
type completeFunc func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
func completeExtFiles(ext ...string) completeFunc {
2023-04-06 17:39:59 +08:00
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
files := make([]string, 0)
for _, e := range ext {
f, err := filepath.Glob(toComplete + "*." + e)
if err != nil {
return nil, cobra.ShellCompDirectiveDefault
}
files = append(files, f...)
}
2023-04-06 18:12:48 +08:00
return files, cobra.ShellCompDirectiveFilterDirs
2023-04-06 17:39:59 +08:00
}
}
2023-12-11 10:57:35 +08:00
func tRun(ctx context.Context, f func(ctx context.Context, c *telegram.Client, kvd kv.KV) error, middlewares ...telegram.Middleware) error {
// init tclient kv
kvd, err := kv.From(ctx).Open(viper.GetString(consts.FlagNamespace))
if err != nil {
return errors.Wrap(err, "open kv storage")
}
o := tclient.Options{
KV: kvd,
Proxy: viper.GetString(consts.FlagProxy),
NTP: viper.GetString(consts.FlagNTP),
ReconnectTimeout: viper.GetDuration(consts.FlagReconnectTimeout),
Test: viper.GetString(consts.FlagTest) != "",
UpdateHandler: nil,
}
2023-12-11 10:57:35 +08:00
client, err := tclient.New(ctx, o, false, middlewares...)
if err != nil {
return errors.Wrap(err, "create client")
}
return tclient.Run(ctx, client, func(ctx context.Context) error {
return f(ctx, client, kvd)
})
}
func migrateLegacyToBolt() (rerr error) {
legacy, err := kv.NewWithMap(DefaultLegacyStorage)
if err != nil {
return errors.Wrap(err, "create legacy kv storage")
}
defer multierr.AppendInvoke(&rerr, multierr.Close(legacy))
bolt, err := kv.NewWithMap(DefaultBoltStorage)
if err != nil {
return errors.Wrap(err, "create bolt kv storage")
}
defer multierr.AppendInvoke(&rerr, multierr.Close(bolt))
meta, err := legacy.MigrateTo()
if err != nil {
return errors.Wrap(err, "migrate legacy to bolt")
}
return bolt.MigrateFrom(meta)
}