gostations/stations.go

257 lines
6.6 KiB
Go
Raw Normal View History

2021-03-16 12:32:59 +00:00
package main
2021-03-16 13:09:27 +00:00
import (
"context"
"encoding/json"
2021-03-16 13:09:27 +00:00
"flag"
"fmt"
"os"
"github.com/gmgauthier/gostations/internal/config"
playerpkg "github.com/gmgauthier/gostations/internal/player"
"github.com/gmgauthier/gostations/internal/radio"
"github.com/gmgauthier/gostations/internal/ui"
2021-03-16 13:09:27 +00:00
)
2021-03-16 12:32:59 +00:00
2021-03-18 22:24:46 +00:00
var version string
2024-07-09 20:29:11 +00:00
func showVersion() {
fmt.Println(version)
}
2024-07-09 20:29:11 +00:00
func precheck() {
p := "mpv"
if v, err := config.Get("player.command"); err == nil && v != "" {
p = v
}
if !playerpkg.IsInstalled(p) {
fmt.Printf("%s is either not installed, or not on your $PATH. Cannot continue.\n", p)
os.Exit(1)
}
}
// runFind implements the "find" subcommand for scripting / non-interactive lookup.
// Usage: gostations find [-c country] [-t tags] ... [-x] [-j]
func runFind(args []string) {
fs := flag.NewFlagSet("find", flag.ExitOnError)
var (
name string
country string
state string
tags string
notok bool
jsonOut bool
)
fs.StringVar(&name, "n", "", "Station name (or identifier).")
fs.StringVar(&country, "c", "", "Home country.")
fs.StringVar(&state, "s", "", "Home state (if in the United States).")
fs.StringVar(&tags, "t", "", "Tag (or comma-separated tag list)")
fs.BoolVar(&notok, "x", false, "If toggled, will show stations that are down")
fs.BoolVar(&jsonOut, "j", false, "Output as JSON array (for scripting)")
fs.BoolVar(&jsonOut, "json", false, "Output as JSON array (for scripting)")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: gostations find [options]\n\nOptions:\n")
fs.PrintDefaults()
}
_ = fs.Parse(args)
if err := config.Init(); err != nil {
fmt.Fprintf(os.Stderr, "config: %v\n", err)
os.Exit(1)
}
stations, err := radio.Search(context.Background(), name, country, state, tags, notok)
if err != nil {
fmt.Fprintf(os.Stderr, "search: %v\n", err)
os.Exit(1)
}
if jsonOut {
b, _ := json.MarshalIndent(stations, "", " ")
fmt.Println(string(b))
return
}
// human/script friendly list (similar to old output)
fmt.Println("...Found Stations...")
for i, s := range stations {
listing := fmt.Sprintf("%-40s %-5s %-5s %s", Short(s.Name, 40), s.Codec, s.Bitrate, s.Url)
fmt.Printf("%d) %s\n", i+1, listing)
}
if len(stations) == 0 {
fmt.Println("(no stations matched)")
}
}
// runPlay implements the "play" subcommand for scripting direct playback.
// It accepts the same search flags or a direct URL as first positional arg.
// Plays the first match (or the URL) using the configured player and blocks.
func runPlay(args []string) {
fs := flag.NewFlagSet("play", flag.ExitOnError)
var (
name string
country string
state string
tags string
notok bool
)
fs.StringVar(&name, "n", "", "Station name (or identifier).")
fs.StringVar(&country, "c", "", "Home country.")
fs.StringVar(&state, "s", "", "Home state (if in the United States).")
fs.StringVar(&tags, "t", "", "Tag (or comma-separated tag list)")
fs.BoolVar(&notok, "x", false, "If toggled, will show stations that are down")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: gostations play [options] [direct-url]\n\nOptions:\n")
fs.PrintDefaults()
}
_ = fs.Parse(args)
pos := fs.Args()
if err := config.Init(); err != nil {
fmt.Fprintf(os.Stderr, "config: %v\n", err)
os.Exit(1)
}
precheck()
var playURL string
if len(pos) > 0 {
playURL = pos[0]
} else {
stations, err := radio.Search(context.Background(), name, country, state, tags, notok)
if err != nil {
fmt.Fprintf(os.Stderr, "search: %v\n", err)
os.Exit(1)
}
if len(stations) == 0 {
fmt.Fprintln(os.Stderr, "no stations found to play")
os.Exit(1)
}
playURL = stations[0].Url
fmt.Printf("Playing first match: %s\n", stations[0].Name)
}
// Use legacy for now (cleaned); will use richer player in TUI/IPC later.
pname := "mpv"
if v, err := config.Get("player.command"); err == nil && v != "" {
pname = v
}
opts := ""
if v, err := config.Get("player.options"); err == nil {
opts = v
}
leg := playerpkg.NewLegacy(pname, opts)
if err := leg.Play(playURL); err != nil {
fmt.Fprintf(os.Stderr, "play error: %v\n", err)
os.Exit(1)
}
}
2024-07-09 20:29:11 +00:00
func main() {
// Early version / help for top level
if len(os.Args) > 1 {
switch os.Args[1] {
case "find":
runFind(os.Args[2:])
return
case "play":
runPlay(os.Args[2:])
return
case "-v", "--version", "version":
showVersion()
return
case "-h", "--help", "help":
printTopHelp()
return
}
}
2021-03-16 13:09:27 +00:00
// Common flags (used for TUI seeding or legacy)
2021-03-16 13:09:27 +00:00
var (
2024-07-09 20:29:11 +00:00
name string
2021-03-16 13:09:27 +00:00
country string
2024-07-09 20:29:11 +00:00
state string
tags string
notok bool
legacy bool
2024-07-09 20:29:11 +00:00
version bool
2021-03-16 13:09:27 +00:00
)
fs := flag.NewFlagSet("gostations", flag.ExitOnError)
fs.StringVar(&name, "n", "", "Station name (or identifier).")
fs.StringVar(&country, "c", "", "Home country.")
fs.StringVar(&state, "s", "", "Home state (if in the United States).")
fs.StringVar(&tags, "t", "", "Tag (or comma-separated tag list)")
fs.BoolVar(&notok, "x", false, "If toggled, will show stations that are down")
fs.BoolVar(&legacy, "legacy", false, "Force old wmenu UI (kept until new TUI is perfect)")
fs.BoolVar(&version, "v", false, "Show version.")
fs.Usage = printTopHelp
_ = fs.Parse(os.Args[1:])
if version {
showVersion()
return
}
precheck()
2021-03-16 12:32:59 +00:00
if err := config.Init(); err != nil {
fmt.Printf("config init: %v\n", err)
os.Exit(1)
}
stations, err := radio.Search(context.Background(), name, country, state, tags, notok)
if err != nil {
fmt.Printf("warning: station search: %v\n", err)
stations = nil
}
if legacy {
// Old wmenu path (gated)
menu := RadioMenu(stations)
if err := menu.Run(); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
return
}
// Default: new TUI (Bubble Tea)
if err := ui.Run(stations); err != nil {
fmt.Printf("TUI error: %v\n", err)
2021-03-16 23:06:20 +00:00
os.Exit(1)
}
}
func printTopHelp() {
fmt.Printf(`Usage:
gostations [options] # Launch interactive TUI (default)
gostations find [options] # Non-interactive station search (scripting)
gostations play [options] [url] # Direct playback (scripting)
gostations -h | --help
gostations -v | --version
Global options:
-n string Station name (or identifier).
-c string Home country.
-s string Home state (if in the United States).
-t string Tag (or comma-separated tag list)
-x If toggled, will show stations that are down
-v Show version.
--legacy Force old wmenu UI (temporary)
Subcommand examples:
gostations find -c "United Kingdom" -t "news" -j
gostations play -c "Gambia"
gostations play http://stream.example.com/radio.mp3
Old top-level behavior without subcommand now defaults to the new TUI.
Use --legacy to force the classic wmenu flow.
`)
2024-07-09 20:29:11 +00:00
}