mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-10-16 22:07:30 +02:00
refactor: split the command dispatch out of main.go
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>
This commit is contained in:
5 files changed
+936
-800
No files matched your search
@@ -0,0 +1,200 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"streamrip-go/internal/app"
|
||||
"streamrip-go/internal/config"
|
||||
)
|
||||
|
||||
// Commands that queue media and rip it: url, file and id.
|
||||
|
||||
func cmdURL(ctx context.Context, cfg *config.Config, gopts globalOptions, args []string) int {
|
||||
if len(args) < 1 {
|
||||
printCommandHelp(os.Stdout, "url")
|
||||
return 2
|
||||
}
|
||||
|
||||
mainApp, err := app.New(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "app init error: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
defer func() { _ = mainApp.Close() }()
|
||||
|
||||
rawArgs := make([]string, 0, len(args))
|
||||
ignoreDB := false
|
||||
for _, arg := range args {
|
||||
if arg == "--force" || arg == "--ignore-db" {
|
||||
ignoreDB = true
|
||||
continue
|
||||
}
|
||||
rawArgs = append(rawArgs, arg)
|
||||
}
|
||||
mainApp.IgnoreDB = ignoreDB || gopts.noDB
|
||||
|
||||
added := 0
|
||||
for _, raw := range rawArgs {
|
||||
if addURLToQueue(ctx, mainApp, raw) {
|
||||
added++
|
||||
}
|
||||
}
|
||||
|
||||
if added == 0 {
|
||||
fmt.Println("nothing to rip")
|
||||
return 0
|
||||
}
|
||||
|
||||
if err = mainApp.Resolve(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "resolve error: %s\n", errorWithActionableHint(err, gopts))
|
||||
return 1
|
||||
}
|
||||
if err = mainApp.Rip(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "rip error: %s\n", errorWithActionableHint(err, gopts))
|
||||
return 1
|
||||
}
|
||||
fmt.Printf("url rip complete (%d item(s))\n", added)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func cmdFile(ctx context.Context, cfg *config.Config, gopts globalOptions, args []string) int {
|
||||
if len(args) < 1 {
|
||||
printCommandHelp(os.Stdout, "file")
|
||||
return 2
|
||||
}
|
||||
|
||||
ignoreDB := false
|
||||
for _, arg := range args[1:] {
|
||||
switch arg {
|
||||
case "--force", "--ignore-db":
|
||||
ignoreDB = true
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "option error: unknown option %q\n", arg)
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "read file error: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
idItems, urls, repeated, jsonInput, err := parseFileInput(content)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "file parse error: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
|
||||
mainApp, err := app.New(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "app init error: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
defer func() { _ = mainApp.Close() }()
|
||||
mainApp.IgnoreDB = ignoreDB || gopts.noDB
|
||||
|
||||
added := 0
|
||||
if jsonInput {
|
||||
fmt.Printf("detected json file. loading %d item(s)\n", len(idItems))
|
||||
for _, item := range idItems {
|
||||
if err = mainApp.AddByID(ctx, item.Source, item.MediaType, item.ID); err != nil {
|
||||
fmt.Printf("add failed: source=%s type=%s id=%s err=%v\n", item.Source, item.MediaType, item.ID, err)
|
||||
continue
|
||||
}
|
||||
added++
|
||||
}
|
||||
} else {
|
||||
if repeated > 0 {
|
||||
fmt.Printf("found %d repeated url(s)\n", repeated)
|
||||
}
|
||||
fmt.Printf("detected list of urls. loading %d item(s)\n", len(urls))
|
||||
for _, raw := range urls {
|
||||
if addURLToQueue(ctx, mainApp, raw) {
|
||||
added++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if added == 0 {
|
||||
fmt.Println("nothing to rip")
|
||||
return 0
|
||||
}
|
||||
|
||||
if err = mainApp.Resolve(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "resolve error: %s\n", errorWithActionableHint(err, gopts))
|
||||
return 1
|
||||
}
|
||||
if err = mainApp.Rip(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "rip error: %s\n", errorWithActionableHint(err, gopts))
|
||||
return 1
|
||||
}
|
||||
fmt.Printf("file rip complete (%d item(s))\n", added)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func cmdID(ctx context.Context, cfg *config.Config, gopts globalOptions, args []string) int {
|
||||
if len(args) < 3 {
|
||||
printCommandHelp(os.Stdout, "id")
|
||||
return 2
|
||||
}
|
||||
|
||||
source := strings.ToLower(strings.TrimSpace(args[0]))
|
||||
mediaType := strings.ToLower(strings.TrimSpace(args[1]))
|
||||
itemID := strings.TrimSpace(args[2])
|
||||
|
||||
opts, err := parseSmokeOptions(args[3:], 0, 4)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "option error: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
if opts.qualitySet {
|
||||
switch source {
|
||||
case "qobuz":
|
||||
if opts.quality < 1 {
|
||||
fmt.Fprintf(os.Stderr, "quality error: qobuz quality must be 1-4\n")
|
||||
return 2
|
||||
}
|
||||
cfg.Session.Qobuz.Quality = opts.quality
|
||||
case "tidal":
|
||||
cfg.Session.Tidal.Quality = opts.quality
|
||||
case "yandex":
|
||||
cfg.Session.Yandex.Quality = opts.quality
|
||||
case "beatport":
|
||||
if opts.quality < 1 {
|
||||
fmt.Fprintf(os.Stderr, "quality error: beatport quality must be 1-4\n")
|
||||
return 2
|
||||
}
|
||||
cfg.Session.Beatport.Quality = opts.quality
|
||||
}
|
||||
}
|
||||
|
||||
mainApp, err := app.New(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "app init error: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
defer func() { _ = mainApp.Close() }()
|
||||
mainApp.IgnoreDB = opts.ignoreDB || gopts.noDB
|
||||
|
||||
if err = mainApp.AddByID(ctx, source, mediaType, itemID); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "add error: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if err = mainApp.Resolve(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "resolve error: %s\n", errorWithActionableHint(err, gopts))
|
||||
return 1
|
||||
}
|
||||
if err = mainApp.Rip(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "rip error: %s\n", errorWithActionableHint(err, gopts))
|
||||
return 1
|
||||
}
|
||||
fmt.Printf("id rip complete: source=%s type=%s id=%s\n", source, mediaType, itemID)
|
||||
|
||||
return 0
|
||||
}
|
||||
Reference in new issue
Block a user