cmd

package
v0.0.0-...-3911b13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AccountCmd = &cli.Command{
	Name:    "accounts",
	Aliases: []string{"acc"},
	Usage:   "list configured accounts",
	Action: func(ctx context.Context, c *cli.Command) error {
		cfg := config.FromContext(ctx)
		accounts := cfg.AllAccounts()
		if len(accounts) == 0 {
			fmt.Println("no accounts configured")
			return nil
		}
		sessionDir, err := config.SessionsDir()
		if err != nil {
			return err
		}
		w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
		fmt.Fprintln(w, "ALIAS\tUSERNAME\tSESSION")
		for _, acc := range accounts {
			state := "-"
			if session.Exists(sessionDir, acc.Username) {
				state = "cached"
			}
			fmt.Fprintf(w, "%s\t%s\t%s\n", acc.Alias, acc.Username, state)
		}
		return w.Flush()
	},
}
View Source
var DownloadCmd = &cli.Command{
	Name:      "download",
	Aliases:   []string{"d"},
	Usage:     "download a file, or the direct child files of a folder, by id or remote path",
	ArgsUsage: "<file_id|path> [file_id|path...]",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:    "output",
			Aliases: []string{"o"},
			Usage:   "output path",
		},
		&cli.IntFlag{
			Name:    "parallel",
			Aliases: []string{"p"},
			Usage:   "parallel connections for ranged download (default 4, 1 disables)",
		},
		&cli.StringFlag{
			Name:    "chunk-min",
			Aliases: []string{"c"},
			Usage:   "minimum file size to use parallel mode, e.g. 32MB (default 32MB)",
		},
		&cli.BoolFlag{
			Name:    "force",
			Aliases: []string{"f"},
			Usage:   "redownload even if local file size matches",
		},
	},
	Action: func(ctx context.Context, c *cli.Command) error {
		if c.Args().Len() < 1 {
			return errors.New("download requires at least one <file_id|path> argument")
		}
		opts := pikpak.DownloadOptions{
			Parallel: c.Int("parallel"),
			Force:    c.Bool("force"),
		}
		if cm := c.String("chunk-min"); cm != "" {
			n, err := parseSize(cm)
			if err != nil {
				return fmt.Errorf("--chunk-min: %w", err)
			}
			opts.ChunkMin = n
		}

		targets := c.Args().Slice()
		output := c.String("output")

		clients := make(map[string]*pikpak.Client)

		getClient := func(alias string) (*pikpak.Client, config.Account, error) {
			if cl, ok := clients[alias]; ok {
				return cl, config.Account{Alias: alias}, nil
			}
			cfg := config.FromContext(ctx)
			acc, err := cfg.FindAccount(alias)
			if err != nil {
				return nil, config.Account{}, err
			}
			sessionDir, err := config.SessionsDir()
			if err != nil {
				return nil, config.Account{}, err
			}
			cl := pikpak.New(acc, sessionDir)
			if err := cl.Login(ctx); err != nil {
				return nil, config.Account{}, err
			}
			clients[alias] = cl
			return cl, acc, nil
		}

		if c.String("account") != "" {
			client, acc, err := clientFromContext(ctx, c)
			if err != nil {
				return err
			}
			fmt.Printf("account: %s\n", acc.Alias)
			var errs []error
			for i, target := range targets {
				if len(targets) > 1 {
					fmt.Printf("\n[%d/%d] downloading: %s\n", i+1, len(targets), target)
				}
				if err := client.Download(ctx, target, output, opts); err != nil {
					fmt.Fprintf(c.ErrWriter, "error downloading %s: %v\n", target, err)
					errs = append(errs, fmt.Errorf("%s: %w", target, err))
				}
			}
			if len(errs) > 0 {
				return fmt.Errorf("failed to download %d of %d files", len(errs), len(targets))
			}
			return nil
		}

		cfg := config.FromContext(ctx)
		allAccounts := cfg.AllAccounts()
		if len(allAccounts) == 0 {
			return fmt.Errorf("no accounts configured")
		}

		var errs []error
		defaultAcc := allAccounts[0]
		for i, target := range targets {
			if len(targets) > 1 {
				fmt.Printf("\n[%d/%d] downloading: %s\n", i+1, len(targets), target)
			}

			var client *pikpak.Client
			var acc config.Account
			if pikpak.IsFileID(target) {

				cl, a, err := resolveFileIDAccount(ctx, c, target)
				if err != nil {
					fmt.Fprintf(c.ErrWriter, "error resolving account for %s: %v\n", target, err)
					errs = append(errs, fmt.Errorf("%s: %w", target, err))
					continue
				}
				client = cl
				acc = a
			} else {

				cl, a, err := getClient(defaultAcc.Alias)
				if err != nil {
					fmt.Fprintf(c.ErrWriter, "error logging in %s: %v\n", defaultAcc.Alias, err)
					errs = append(errs, fmt.Errorf("%s: %w", target, err))
					continue
				}
				client = cl
				acc = a
			}
			fmt.Printf("account: %s\n", acc.Alias)
			if err := client.Download(ctx, target, output, opts); err != nil {
				fmt.Fprintf(c.ErrWriter, "error downloading %s: %v\n", target, err)
				errs = append(errs, fmt.Errorf("%s: %w", target, err))
			}
		}
		if len(errs) > 0 {
			return fmt.Errorf("failed to download %d of %d files", len(errs), len(targets))
		}
		return nil
	},
}
View Source
var FileCmd = &cli.Command{
	Name:    "file",
	Aliases: []string{"f"},
	Usage:   "file manage",
	Commands: []*cli.Command{
		listCmd,
		DownloadCmd,
		deleteCmd,
		clearCmd,
	},
}
View Source
var QuotaCmd = &cli.Command{
	Name:    "quota",
	Aliases: []string{"q"},
	Usage:   "query quota for account",
	Action: func(ctx context.Context, c *cli.Command) error {
		cfg := config.FromContext(ctx)
		sessionDir, err := config.SessionsDir()
		if err != nil {
			return err
		}
		var targets []config.Account
		if alias := c.String("account"); alias != "" {
			acc, err := cfg.FindAccount(alias)
			if err != nil {
				return err
			}
			targets = []config.Account{acc}
		} else {
			targets = cfg.AllAccounts()
		}
		if len(targets) == 0 {
			fmt.Println("no accounts configured")
			return nil
		}

		state, err := pool.LoadState()
		if err != nil {
			return err
		}

		w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
		fmt.Fprintln(w, "ACCOUNT\tCLOUD_DOWNLOAD(REMAINING/TOTAL)\tSTORAGE")
		for _, acc := range targets {
			client := pikpak.New(acc, sessionDir)
			if err := client.Login(ctx); err != nil {
				fmt.Fprintf(w, "%s\tERROR\t%s\n", acc.Alias, err)
				continue
			}
			q, err := client.Quota(ctx)
			if err != nil {
				fmt.Fprintf(w, "%s\tERROR\t%s\n", acc.Alias, err)
				continue
			}

			as := state.GetOrCreate(acc.Alias)
			as.QuotaCache = &pool.QuotaSnapshot{
				CloudDownloadLimit: q.Quotas.CloudDownload.Limit,
				CloudDownloadUsage: q.Quotas.CloudDownload.Usage,
				UpdatedAt:          time.Now(),
			}

			fmt.Fprintf(w, "%s\t%d/%d\t%s/%s\n",
				acc.Alias,
				q.Quotas.CloudDownload.Limit-q.Quotas.CloudDownload.Usage,
				q.Quotas.CloudDownload.Limit,
				pikpak.ByteSize(q.Quota.Usage),
				pikpak.ByteSize(q.Quota.Limit),
			)
		}
		if err := w.Flush(); err != nil {
			return err
		}
		return pool.SaveState(state)
	},
}
View Source
var ServeCmd = &cli.Command{
	Name:  "serve",
	Usage: "run an HTTP server that accepts offline task submissions",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:    "addr",
			Aliases: []string{"l"},
			Value:   "127.0.0.1:8080",
			Usage:   "listen address (use 0.0.0.0:8080 for LAN access; no auth)",
		},
		&cli.StringFlag{
			Name:    "folder",
			Aliases: []string{"f"},
			Usage:   "destination folder: path (e.g. /movies/2024) or folder-id",
		},
	},
	Action: serveAction,
}

ServeCmd runs a long-lived HTTP server that accepts offline task submissions from the browser extension or any HTTP client.

POST /api/tasks   body: {"hash": "<40-hex>"} | {"magnet": "magnet:?..."} | {"url": "https://..."}
                  200/201 queued, 409 duplicate (already submitted), 400 bad input, 500 server error
GET  /healthz     liveness probe
View Source
var TaskCmd = &cli.Command{
	Name:    "task",
	Aliases: []string{"t"},
	Usage:   "manage offline tasks",
	Commands: []*cli.Command{
		taskAddCmd,
		taskListCmd,
		taskDeleteCmd,
		taskClearCmd,
	},
}
View Source
var TrashCmd = &cli.Command{
	Name:  "trash",
	Usage: "manage trash",
	Commands: []*cli.Command{
		emptyCmd,
	},
}

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL