feat(cli): main flow with interactive and named-arg modes
This commit is contained in:
parent
2f93726ec9
commit
cdf64f4c3e
1 changed files with 158 additions and 2 deletions
160
cli/main.go
160
cli/main.go
|
|
@ -1,7 +1,163 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("msdl — Windows ISO downloader")
|
||||
if err := run(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
fs := flag.NewFlagSet("msdl", flag.ContinueOnError)
|
||||
fs.SetOutput(os.Stderr)
|
||||
fs.Usage = func() {
|
||||
fmt.Fprintln(os.Stderr, `msdl — Windows ISO downloader
|
||||
|
||||
Usage:
|
||||
msdl [search terms] interactive: filter + pick product, pick language
|
||||
msdl --id 3262 skip product picker
|
||||
msdl --id 3262 --lang "English (United States)" no prompts, print URL directly
|
||||
msdl --eval [slug] evaluation ISOs (server-2025, win11-ent, ...)
|
||||
msdl --list list all products and exit
|
||||
|
||||
Flags:`)
|
||||
fs.PrintDefaults()
|
||||
}
|
||||
|
||||
productID := fs.String("id", "", "consumer product ID (skips product picker)")
|
||||
langFlag := fs.String("lang", "", `language name, e.g. "English (United States)"`)
|
||||
evalMode := fs.Bool("eval", false, "fetch evaluation ISOs")
|
||||
listMode := fs.Bool("list", false, "list all products and exit")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
query := strings.Join(fs.Args(), " ")
|
||||
|
||||
if *listMode {
|
||||
fmt.Fprintln(os.Stderr, "Consumer products:")
|
||||
for _, p := range consumerProducts {
|
||||
fmt.Fprintf(os.Stderr, " %-6s %s\n", p.ID, p.Name)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "\nEvaluation products:")
|
||||
for _, p := range evalProducts {
|
||||
fmt.Fprintf(os.Stderr, " %-20s %s\n", p.Slug, p.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if *evalMode {
|
||||
return runEval(query)
|
||||
}
|
||||
return runConsumer(*productID, query, *langFlag)
|
||||
}
|
||||
|
||||
func runConsumer(productID, query, langName string) error {
|
||||
var product Product
|
||||
|
||||
if productID != "" {
|
||||
p, ok := findProductByID(productID)
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown product ID %q — run msdl --list to see all products", productID)
|
||||
}
|
||||
product = p
|
||||
} else {
|
||||
candidates := consumerProducts
|
||||
if query != "" {
|
||||
candidates = searchProducts(query)
|
||||
if len(candidates) == 0 {
|
||||
return fmt.Errorf("no products match %q — run msdl --list to see all products", query)
|
||||
}
|
||||
}
|
||||
if len(candidates) == 1 {
|
||||
product = candidates[0]
|
||||
fmt.Fprintf(os.Stderr, "Selected: %s\n", product.Name)
|
||||
} else {
|
||||
var err error
|
||||
product, err = pickProduct(candidates)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Setting up Microsoft session...\n")
|
||||
client, sessionID := newSession()
|
||||
|
||||
langs, err := fetchLanguages(client, sessionID, product.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching languages for %s: %w", product.Name, err)
|
||||
}
|
||||
|
||||
var lang Language
|
||||
if langName != "" {
|
||||
for _, l := range langs {
|
||||
if strings.EqualFold(l.Language, langName) {
|
||||
lang = l
|
||||
break
|
||||
}
|
||||
}
|
||||
if lang.ID == "" {
|
||||
return fmt.Errorf("language %q not available for %s", langName, product.Name)
|
||||
}
|
||||
} else {
|
||||
lang, err = pickLanguage(langs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Fetching download link...\n")
|
||||
links, err := fetchDownloadLinks(client, sessionID, product.ID, lang.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching download links: %w", err)
|
||||
}
|
||||
|
||||
for _, link := range links {
|
||||
fmt.Println(link.URI)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runEval(slug string) error {
|
||||
var ep EvalProduct
|
||||
|
||||
if slug != "" {
|
||||
p, ok := findEvalProduct(slug)
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown eval product %q — valid slugs: server-2025, server-2022, server-2019, server-2016, win11-ent", slug)
|
||||
}
|
||||
ep = p
|
||||
} else {
|
||||
var err error
|
||||
ep, err = pickEvalProduct(evalProducts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Fetching eval links for %s...\n", ep.Name)
|
||||
links, err := fetchEvalLinks(ep.EvalURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching eval links for %s: %w", ep.Name, err)
|
||||
}
|
||||
if len(links) == 0 {
|
||||
return fmt.Errorf("no eval links returned for %s", ep.Slug)
|
||||
}
|
||||
|
||||
for _, link := range links {
|
||||
if link.Arch != "" {
|
||||
fmt.Printf("%s\t%s\n", link.Arch, link.URL)
|
||||
} else {
|
||||
fmt.Println(link.URL)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue