mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-24 14:47:23 +00:00
feat(cli)!: deprecate speaker-based station search in favour of find
The `find` family runs the search inside the CLI, querying the radio provider's public API directly (no speaker cloud, no soundtouch-service). Make it the canonical path and deprecate the speaker-based search family. - Add `find-tunein` and `find-radiobrowser` siblings; refactor the find actions onto a shared `runFind` helper (all support `--more`). - Rename the unreleased `search-radiobrowser` to `find-radiobrowser`. - Deprecate `search`, `search-tunein`, `search-pandora`, `search-spotify`: they keep working but print a stderr deprecation notice (new `PrintDeprecation` helper) pointing at the `find*` replacement. Pandora and Spotify have no built-in equivalent yet (they need the speaker + account), so their notices say so. - Docs: lead with the `find` family as recommended; mark the speaker-based search commands deprecated; drop the misleading "service-side" wording in favour of "built-in / queries the provider directly". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
21efb412fe
commit
c7eda7ed7b
@@ -12,6 +12,12 @@ import (
|
||||
|
||||
// searchStations handles searching for stations across different sources
|
||||
func searchStations(c *cli.Context) error {
|
||||
PrintDeprecation(
|
||||
"station search",
|
||||
"It asks the speaker to search, which fails when the speaker's cloud is gone.",
|
||||
`soundtouch-cli station find --provider tunein --query "<your search>"`,
|
||||
)
|
||||
|
||||
source := c.String("source")
|
||||
sourceAccount := c.String("source-account")
|
||||
searchTerm := c.String("query")
|
||||
@@ -51,6 +57,12 @@ func searchStations(c *cli.Context) error {
|
||||
|
||||
// searchTuneIn handles searching TuneIn specifically
|
||||
func searchTuneIn(c *cli.Context) error {
|
||||
PrintDeprecation(
|
||||
"station search-tunein",
|
||||
"It asks the speaker to search, which fails when the speaker's cloud is gone.",
|
||||
`soundtouch-cli station find-tunein --query "<your search>"`,
|
||||
)
|
||||
|
||||
searchTerm := c.String("query")
|
||||
|
||||
if searchTerm == "" {
|
||||
@@ -86,6 +98,12 @@ func searchTuneIn(c *cli.Context) error {
|
||||
|
||||
// searchPandora handles searching Pandora specifically
|
||||
func searchPandora(c *cli.Context) error {
|
||||
PrintDeprecation(
|
||||
"station search-pandora",
|
||||
"There is no built-in Pandora search yet (it requires the speaker and your account).",
|
||||
"",
|
||||
)
|
||||
|
||||
sourceAccount := c.String("source-account")
|
||||
searchTerm := c.String("query")
|
||||
|
||||
@@ -127,6 +145,12 @@ func searchPandora(c *cli.Context) error {
|
||||
|
||||
// searchSpotify handles searching Spotify specifically
|
||||
func searchSpotify(c *cli.Context) error {
|
||||
PrintDeprecation(
|
||||
"station search-spotify",
|
||||
"There is no built-in Spotify search yet (it requires the speaker and your account).",
|
||||
"",
|
||||
)
|
||||
|
||||
sourceAccount := c.String("source-account")
|
||||
searchTerm := c.String("query")
|
||||
|
||||
@@ -531,31 +555,18 @@ func bmxNavCursor(section *models.BmxNavSection) string {
|
||||
return parsed.Query().Get("cursor")
|
||||
}
|
||||
|
||||
// searchService is the action for `station search` with --provider / --query / --more.
|
||||
// It uses the service-side stations package (works even when the speaker's cloud is dead).
|
||||
func searchService(c *cli.Context) error {
|
||||
providerStr := c.String("provider")
|
||||
query := c.String("query")
|
||||
more := c.Bool("more")
|
||||
|
||||
// runFind performs a built-in station search for the given provider and
|
||||
// prints the results. The search runs inside the CLI itself, querying the
|
||||
// radio provider's public API directly — it needs neither the speaker's
|
||||
// cloud nor a running soundtouch-service. When more is true it follows up
|
||||
// to three additional result pages while a next cursor is available.
|
||||
func runFind(provider stations.Provider, label, query string, more bool) error {
|
||||
if query == "" {
|
||||
PrintError("Search query is required")
|
||||
return fmt.Errorf("search query cannot be empty")
|
||||
}
|
||||
|
||||
var provider stations.Provider
|
||||
|
||||
switch strings.ToLower(providerStr) {
|
||||
case "tunein":
|
||||
provider = stations.ProviderTuneIn
|
||||
case "radiobrowser":
|
||||
provider = stations.ProviderRadioBrowser
|
||||
default:
|
||||
PrintError(fmt.Sprintf("Unknown provider %q: must be 'tunein' or 'radiobrowser'", providerStr))
|
||||
return fmt.Errorf("unknown provider: %s", providerStr)
|
||||
}
|
||||
|
||||
fmt.Printf("Searching %s for: %s\n", providerStr, query)
|
||||
fmt.Printf("Searching %s for: %s\n", label, query)
|
||||
|
||||
resp, err := stations.Search(provider, query)
|
||||
if err != nil {
|
||||
@@ -569,7 +580,6 @@ func searchService(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Follow up to 3 additional pages while a next cursor is available.
|
||||
const maxExtraPages = 3
|
||||
for page := 0; page < maxExtraPages; page++ {
|
||||
// Find a cursor from any section that has one.
|
||||
@@ -599,25 +609,36 @@ func searchService(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// searchServiceRadioBrowser is the action for `station search-radiobrowser`.
|
||||
// It searches via the service-side RadioBrowser backend.
|
||||
func searchServiceRadioBrowser(c *cli.Context) error {
|
||||
query := c.String("query")
|
||||
// findStations is the action for the unified `station find` with
|
||||
// --provider / --query / --more.
|
||||
func findStations(c *cli.Context) error {
|
||||
providerStr := c.String("provider")
|
||||
|
||||
if query == "" {
|
||||
PrintError("Search query is required")
|
||||
return fmt.Errorf("search query cannot be empty")
|
||||
var (
|
||||
provider stations.Provider
|
||||
label string
|
||||
)
|
||||
|
||||
switch strings.ToLower(providerStr) {
|
||||
case "tunein":
|
||||
provider, label = stations.ProviderTuneIn, "TuneIn"
|
||||
case "radiobrowser":
|
||||
provider, label = stations.ProviderRadioBrowser, "Radio Browser"
|
||||
default:
|
||||
PrintError(fmt.Sprintf("Unknown provider %q: must be 'tunein' or 'radiobrowser'", providerStr))
|
||||
return fmt.Errorf("unknown provider: %s", providerStr)
|
||||
}
|
||||
|
||||
fmt.Printf("Searching Radio Browser for: %s\n", query)
|
||||
return runFind(provider, label, c.String("query"), c.Bool("more"))
|
||||
}
|
||||
|
||||
resp, err := stations.Search(stations.ProviderRadioBrowser, query)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Search failed: %v", err))
|
||||
return err
|
||||
}
|
||||
// findTuneIn is the action for `station find-tunein` (built-in TuneIn search).
|
||||
func findTuneIn(c *cli.Context) error {
|
||||
return runFind(stations.ProviderTuneIn, "TuneIn", c.String("query"), c.Bool("more"))
|
||||
}
|
||||
|
||||
printBmxNavResults(resp)
|
||||
|
||||
return nil
|
||||
// findRadioBrowser is the action for `station find-radiobrowser`
|
||||
// (built-in Radio Browser search).
|
||||
func findRadioBrowser(c *cli.Context) error {
|
||||
return runFind(stations.ProviderRadioBrowser, "Radio Browser", c.String("query"), c.Bool("more"))
|
||||
}
|
||||
|
||||
@@ -340,6 +340,22 @@ func PrintWarning(message string) {
|
||||
fmt.Printf("⚠️ %s\n", message)
|
||||
}
|
||||
|
||||
// PrintDeprecation prints a deprecation notice to stderr (so it does not
|
||||
// pollute piped stdout output). reason explains why the command is going
|
||||
// away; newUsage is an optional replacement example — pass "" when there
|
||||
// is no replacement yet.
|
||||
func PrintDeprecation(command, reason, newUsage string) {
|
||||
fmt.Fprintf(os.Stderr, "⚠️ '%s' is deprecated and will be removed in a future release.\n", command)
|
||||
|
||||
if reason != "" {
|
||||
fmt.Fprintf(os.Stderr, " %s\n", reason)
|
||||
}
|
||||
|
||||
if newUsage != "" {
|
||||
fmt.Fprintf(os.Stderr, " Use instead:\n %s\n", newUsage)
|
||||
}
|
||||
}
|
||||
|
||||
// showVersionInfo displays detailed version information including build details
|
||||
func showVersionInfo(_ *cli.Context) error {
|
||||
fmt.Printf("%s version %s\n", os.Args[0], version)
|
||||
|
||||
+67
-39
@@ -589,9 +589,72 @@ func main() {
|
||||
Aliases: []string{"st"},
|
||||
Usage: "Search and manage stations",
|
||||
Subcommands: []*cli.Command{
|
||||
// Built-in search ("find" family): runs inside the CLI,
|
||||
// querying the radio provider's public API directly. No
|
||||
// speaker cloud and no soundtouch-service required.
|
||||
{
|
||||
Name: "find",
|
||||
Usage: "Find stations directly (built-in tunein or radiobrowser search; no speaker needed)",
|
||||
Action: findStations,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "provider",
|
||||
Usage: "Station provider: tunein or radiobrowser",
|
||||
Value: "tunein",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "query",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "Search query",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "more",
|
||||
Usage: "Follow up to 3 additional result pages when available",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "find-tunein",
|
||||
Usage: "Find TuneIn stations directly (built-in search; no speaker needed)",
|
||||
Action: findTuneIn,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "query",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "Search query",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "more",
|
||||
Usage: "Follow up to 3 additional result pages when available",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "find-radiobrowser",
|
||||
Usage: "Find Radio Browser stations directly (built-in search; no speaker needed)",
|
||||
Action: findRadioBrowser,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "query",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "Search query",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "more",
|
||||
Usage: "Follow up to 3 additional result pages when available",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Deprecated speaker-based search commands. They ask the
|
||||
// speaker to search, which fails once its cloud is gone.
|
||||
// Prefer the "find" family above. Kept for now; each emits
|
||||
// a deprecation notice on stderr.
|
||||
{
|
||||
Name: "search",
|
||||
Usage: "Search for stations and content",
|
||||
Usage: "[DEPRECATED] Search via the speaker; use 'station find' instead",
|
||||
Action: searchStations,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -612,31 +675,9 @@ func main() {
|
||||
},
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "find",
|
||||
Usage: "Find stations via the AfterTouch service (tunein or radiobrowser)",
|
||||
Action: searchService,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "provider",
|
||||
Usage: "Station provider: tunein or radiobrowser",
|
||||
Value: "tunein",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "query",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "Search query",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "more",
|
||||
Usage: "Follow up to 3 additional result pages when available",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "search-tunein",
|
||||
Usage: "Search TuneIn stations",
|
||||
Usage: "[DEPRECATED] Search TuneIn via the speaker; use 'station find-tunein' instead",
|
||||
Action: searchTuneIn,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -650,7 +691,7 @@ func main() {
|
||||
},
|
||||
{
|
||||
Name: "search-pandora",
|
||||
Usage: "Search Pandora stations",
|
||||
Usage: "[DEPRECATED] Search Pandora via the speaker (no built-in equivalent yet)",
|
||||
Action: searchPandora,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -669,7 +710,7 @@ func main() {
|
||||
},
|
||||
{
|
||||
Name: "search-spotify",
|
||||
Usage: "Search Spotify content",
|
||||
Usage: "[DEPRECATED] Search Spotify via the speaker (no built-in equivalent yet)",
|
||||
Action: searchSpotify,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -686,19 +727,6 @@ func main() {
|
||||
},
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "search-radiobrowser",
|
||||
Usage: "Search Radio Browser stations via the AfterTouch service",
|
||||
Action: searchServiceRadioBrowser,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "query",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "Search query",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "Add station and play immediately",
|
||||
|
||||
@@ -904,25 +904,63 @@ Search for and manage radio stations and streaming content.
|
||||
|
||||
Search and manage stations.
|
||||
|
||||
##### Built-in search: the `find` family (recommended)
|
||||
|
||||
The `find` commands run the search **inside the CLI itself**, querying the
|
||||
radio provider's public API directly. They need **neither the speaker's
|
||||
cloud nor a running `soundtouch-service`**, and they don't require a
|
||||
reachable speaker (`--host`) to search — so they keep working even after
|
||||
the speaker's original cloud is gone. This is the recommended way to
|
||||
search.
|
||||
|
||||
- `station find --provider tunein|radiobrowser --query <term>` — unified
|
||||
built-in search. `--provider` defaults to `tunein`.
|
||||
- `station find-tunein --query <term>` — TuneIn sibling
|
||||
(= `find --provider tunein`).
|
||||
- `station find-radiobrowser --query <term>` — Radio Browser sibling
|
||||
(= `find --provider radiobrowser`).
|
||||
- `… --more` — follow up to three additional result pages when available
|
||||
(both TuneIn and Radio Browser paginate).
|
||||
|
||||
Results include each station's playback `Location`, which you can feed to
|
||||
`source tunein` (TuneIn) or a preset/play flow.
|
||||
|
||||
```bash
|
||||
# Search across any source
|
||||
# Unified built-in search (no speaker required)
|
||||
soundtouch-cli station find --provider tunein --query "jazz"
|
||||
|
||||
# TuneIn sibling
|
||||
soundtouch-cli station find-tunein --query "jazz"
|
||||
|
||||
# Radio Browser, walking extra result pages
|
||||
soundtouch-cli station find-radiobrowser --query "jazz" --more
|
||||
```
|
||||
|
||||
##### Deprecated: speaker-based search
|
||||
|
||||
These commands ask the **speaker** to search, which only works while the
|
||||
speaker's cloud source is reachable. They are **deprecated** — each prints
|
||||
a deprecation notice — and will be removed in a future release. Prefer the
|
||||
`find` family above. There is no built-in equivalent for Pandora or
|
||||
Spotify *yet*; those still require the speaker and your account.
|
||||
|
||||
```bash
|
||||
# [DEPRECATED] Search across any source via the speaker → use `station find`
|
||||
soundtouch-cli --host <device> station search --source <SOURCE> --query <SEARCH_TERM>
|
||||
|
||||
# Search TuneIn specifically
|
||||
# [DEPRECATED] Search TuneIn via the speaker → use `station find-tunein`
|
||||
soundtouch-cli --host <device> station search-tunein --query <SEARCH_TERM>
|
||||
|
||||
# Search Pandora specifically (requires account)
|
||||
# [DEPRECATED] Search Pandora via the speaker (no built-in equivalent yet)
|
||||
soundtouch-cli --host <device> station search-pandora --source-account <ACCOUNT> --query <SEARCH_TERM>
|
||||
|
||||
# Search Spotify specifically (requires account)
|
||||
# [DEPRECATED] Search Spotify via the speaker (no built-in equivalent yet)
|
||||
soundtouch-cli --host <device> station search-spotify --source-account <ACCOUNT> --query <SEARCH_TERM>
|
||||
```
|
||||
|
||||
# Find stations via the AfterTouch service (TuneIn or Radio Browser; no live cloud needed)
|
||||
soundtouch-cli station find --provider <tunein|radiobrowser> --query <SEARCH_TERM> [--more]
|
||||
|
||||
# Search Radio Browser via the AfterTouch service
|
||||
soundtouch-cli station search-radiobrowser --query <SEARCH_TERM>
|
||||
##### Manage stations
|
||||
|
||||
```bash
|
||||
# Add station and play immediately
|
||||
soundtouch-cli --host <device> station add --source <SOURCE> --token <TOKEN> --name <NAME>
|
||||
|
||||
@@ -933,51 +971,6 @@ soundtouch-cli --host <device> station remove --source <SOURCE> --location <LOCA
|
||||
soundtouch-cli --host <device> station list --source <SOURCE> [--source-account <ACCOUNT>]
|
||||
```
|
||||
|
||||
**Search Examples:**
|
||||
```bash
|
||||
# Search TuneIn for jazz stations
|
||||
soundtouch-cli --host 192.0.2.10 station search-tunein --query "jazz"
|
||||
|
||||
# Search Pandora for Taylor Swift
|
||||
soundtouch-cli --host 192.0.2.10 station search-pandora --source-account myuser123 --query "Taylor Swift"
|
||||
|
||||
# Search Spotify for workout playlists
|
||||
soundtouch-cli --host 192.0.2.10 station search-spotify --source-account spotify_user --query "workout playlist"
|
||||
|
||||
# General search across any source
|
||||
soundtouch-cli --host 192.0.2.10 station search --source TUNEIN --query "classic rock"
|
||||
```
|
||||
|
||||
##### Service-side search: `station find` and `station search-radiobrowser`
|
||||
|
||||
The commands above (`search`, `search-tunein`, …) ask the **speaker** to
|
||||
search, which only works while the speaker's cloud source is reachable.
|
||||
`station find` and `station search-radiobrowser` instead run the search
|
||||
**in AfterTouch itself**, so they work even when the speaker's original
|
||||
cloud is gone — and they don't require a reachable speaker (`--host`) to
|
||||
search:
|
||||
|
||||
- `station find --provider tunein|radiobrowser --query <term>` — unified
|
||||
service-side search. `--provider` defaults to `tunein`.
|
||||
- `station find … --more` — follow up to three additional result pages
|
||||
when more are available (Radio Browser and TuneIn both paginate).
|
||||
- `station search-radiobrowser --query <term>` — Radio Browser sibling,
|
||||
equivalent to `station find --provider radiobrowser`.
|
||||
|
||||
Results include each station's playback `Location`, which you can feed to
|
||||
`source tunein` (TuneIn) or a preset/play flow.
|
||||
|
||||
```bash
|
||||
# Service-side TuneIn search (no speaker required to search)
|
||||
soundtouch-cli station find --provider tunein --query "jazz"
|
||||
|
||||
# Service-side Radio Browser search, walking extra result pages
|
||||
soundtouch-cli station find --provider radiobrowser --query "jazz" --more
|
||||
|
||||
# Radio Browser sibling (same as: station find --provider radiobrowser)
|
||||
soundtouch-cli station search-radiobrowser --query "VRT"
|
||||
```
|
||||
|
||||
**Station Management Examples:**
|
||||
```bash
|
||||
# Add a station found from search results (use token from search output)
|
||||
@@ -1001,8 +994,8 @@ soundtouch-cli --host 192.0.2.10 station remove \
|
||||
|
||||
**Workflow Example - Discover and Play New Content:**
|
||||
```bash
|
||||
# 1. Search for content
|
||||
soundtouch-cli --host 192.0.2.10 station search-tunein --query "smooth jazz"
|
||||
# 1. Search for content (built-in, no speaker needed)
|
||||
soundtouch-cli station find-tunein --query "smooth jazz"
|
||||
|
||||
# 2. Add interesting station from results (copy token from output)
|
||||
soundtouch-cli --host 192.0.2.10 station add \
|
||||
|
||||
Reference in New Issue
Block a user