mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-10-16 22:07:30 +02:00
Completes the restructuring started when main() became run() int. main.go goes
from 1217 lines to 130: global option handling, config loading, signal setup,
and a dispatch switch that delegates to one function per command.
The command bodies move to cmd_rip.go (url, file, id), cmd_search.go (search,
lastfm), cmd_config.go (config, database) and cmd_smoke.go (the developer smoke
commands), each taking (ctx, cfg, gopts, args) and returning an exit code.
They also stop reading the process global. Previously run() did
os.Args = append([]string{os.Args[0], gopts.command}, gopts.commandArgs...)
and the arms then indexed back into os.Args[2:], os.Args[5:] and so on, so the
already-parsed options were round-tripped through os.Args. The arms now take
the parsed gopts.commandArgs directly, which is a uniform index shift of two
(os.Args[5:] -> args[3:], len(os.Args) < 5 -> len(args) < 3).
No behaviour change. Verified by running each command's argument validation and
help path against the built binary, plus the existing suite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
3.4 KiB
Go
129 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"streamrip-go/internal/config"
|
|
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
// Commands that inspect local state: config and database.
|
|
|
|
func cmdConfig(_ context.Context, cfg *config.Config, _ globalOptions, args []string) int {
|
|
var err error
|
|
if len(args) < 1 {
|
|
printCommandHelp(os.Stdout, "config")
|
|
return 2
|
|
}
|
|
switch args[0] {
|
|
case "open":
|
|
vim := false
|
|
for _, arg := range args[1:] {
|
|
switch arg {
|
|
case "-v", "--vim":
|
|
vim = true
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "option error: unknown option %q\n", arg)
|
|
return 2
|
|
}
|
|
}
|
|
fmt.Printf("opening file at %s\n", cfg.Path)
|
|
if err = openConfigInEditor(cfg.Path, vim); err != nil {
|
|
fmt.Fprintf(os.Stderr, "open config error: %v\n", err)
|
|
return 1
|
|
}
|
|
case "reset":
|
|
yes := false
|
|
for _, arg := range args[1:] {
|
|
switch arg {
|
|
case "-y", "--yes":
|
|
yes = true
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "option error: unknown option %q\n", arg)
|
|
return 2
|
|
}
|
|
}
|
|
if !yes {
|
|
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
|
fmt.Fprintln(os.Stderr, "reset requires --yes in non-interactive mode")
|
|
return 2
|
|
}
|
|
ok, askErr := promptYesNo(fmt.Sprintf("Are you sure you want to reset the config file at %s? [y/N]: ", cfg.Path))
|
|
if askErr != nil {
|
|
fmt.Fprintf(os.Stderr, "prompt error: %v\n", askErr)
|
|
return 1
|
|
}
|
|
if !ok {
|
|
fmt.Println("reset aborted")
|
|
return 0
|
|
}
|
|
}
|
|
def := config.DefaultConfigData()
|
|
// Separate copies: assigning the same value to both halves made
|
|
// File.Qobuz.Secrets and Session.Qobuz.Secrets share one backing
|
|
// array.
|
|
cfg.File = config.CloneConfigData(def)
|
|
cfg.Session = config.CloneConfigData(def)
|
|
if err = cfg.SaveFile(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "reset config error: %v\n", err)
|
|
return 1
|
|
}
|
|
fmt.Printf("reset the config file at %s\n", cfg.Path)
|
|
case "path":
|
|
if len(args) > 1 {
|
|
fmt.Fprintf(os.Stderr, "option error: unexpected argument %q\n", args[1])
|
|
return 2
|
|
}
|
|
fmt.Printf("config path: '%s'\n", cfg.Path)
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "unknown config command: %s\n", args[0])
|
|
return 2
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func cmdDatabase(_ context.Context, cfg *config.Config, _ globalOptions, args []string) int {
|
|
if len(args) < 2 || args[0] != "browse" {
|
|
printCommandHelp(os.Stdout, "database")
|
|
return 2
|
|
}
|
|
table := strings.ToLower(strings.TrimSpace(args[1]))
|
|
switch table {
|
|
case "downloads":
|
|
rows, listErr := listDownloadsRows(cfg.Session.Database.DownloadsPath)
|
|
if listErr != nil {
|
|
fmt.Fprintf(os.Stderr, "database browse error: %v\n", listErr)
|
|
return 1
|
|
}
|
|
fmt.Println("downloads database")
|
|
fmt.Println("row id")
|
|
for i, id := range rows {
|
|
fmt.Printf("%02d %s\n", i, id)
|
|
}
|
|
case "failed":
|
|
rows, listErr := listFailedRows(cfg.Session.Database.FailedDownloadsPath)
|
|
if listErr != nil && isNoSuchTableErr(listErr) && cfg.Session.Database.FailedDownloadsPath != cfg.Session.Database.DownloadsPath {
|
|
rows, listErr = listFailedRows(cfg.Session.Database.DownloadsPath)
|
|
}
|
|
if listErr != nil {
|
|
fmt.Fprintf(os.Stderr, "database browse error: %v\n", listErr)
|
|
return 1
|
|
}
|
|
fmt.Println("failed downloads database")
|
|
fmt.Println("row source media_type id")
|
|
for i, row := range rows {
|
|
fmt.Printf("%02d %s %s %s\n", i, row.Source, row.MediaType, row.ID)
|
|
}
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "invalid database %q. choose downloads or failed\n", table)
|
|
return 2
|
|
}
|
|
|
|
return 0
|
|
}
|