diff --git a/README.md b/README.md index 4205a291..cc9ef9a8 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,7 @@ To enable [fuzzy search](https://en.wikipedia.org/wiki/Approximate_string_matchi - `get`: Get Spotify data (playlist/album/artist data, user's data, etc) - `playback`: Interact with the playback (start a playback, play-pause, next, etc) +- `search`: Search spotify - `connect`: Connect to a Spotify device - `like`: Like currently playing track - `authenticate`: Authenticate the application @@ -287,6 +288,18 @@ For more details, run `spotify_player -h` or `spotify_player {command} -h`, in w - When using the CLI for the first time, you'll need to run `spotify_player authenticate` to authenticate the application beforehand. - Under the hood, CLI command is handled by sending requests to a `spotify_player` client socket running on port `client_port`, [a general application configuration](https://github.com/aome510/spotify-player/blob/master/docs/config.md#general) with a default value of `8080`. If there is no running application's instance, a new client will be created upon handling the CLI commands, which increases the latency of the command. +#### Scripting + +The `spotify_player` command-line interface makes scripting easy. +With the `search` subcommand, you can search Spotify and retrieve data in JSON format, enabling queries with tools like [jq](https://jqlang.github.io/jq/). + +Here’s an example of starting playback for the first track from a search query: + +```sh +read -p "Search spotify: " query +spotify_player playback start track --id $(spotify_player search "$query" | jq '.tracks.[0].id' | xargs) +``` + ## Commands To go to the shortcut help page, press `?` or `C-h` (default shortcuts for `OpenCommandHelp` command). diff --git a/docs/config.md b/docs/config.md index 410a7b32..239a6b77 100644 --- a/docs/config.md +++ b/docs/config.md @@ -6,6 +6,7 @@ - [Notes](#notes) - [Media control](#media-control) - [Player event hook command](#player-event-hook-command) + - [Client id command](#client-id-command) - [Device configurations](#device-configurations) - [Layout configurations](#layout-configurations) - [Themes](#themes) @@ -123,6 +124,17 @@ case "$1" in esac ``` +### Client id command + +If you prefer not to include your own `client_id` directly in your configuration, you can retrieve it at runtime using the `client_id_command` option. + +If specified, `client_id_command` should be an object with two fields `command` and `args`, just like `player_event_hook_command`. +For example to read your client_id from a file your could use `client_id_command = { command = "cat", args = ["/path/to/file"] }` + +> [!NOTE] +> When passing a path as an argument, always use the full path. +> The `~` symbol will not automatically expand to your home directory. + ### Device configurations The configuration options for the [Librespot](https://github.com/librespot-org/librespot) integrated device are specified under the `[device]` section in the `app.toml` file: diff --git a/examples/theme.toml b/examples/theme.toml index f9e05b4c..eb5b7d27 100644 --- a/examples/theme.toml +++ b/examples/theme.toml @@ -178,3 +178,91 @@ bright_blue = "#839496" bright_magenta = "#ff007c" bright_cyan = "#93a1a1" bright_white = "#fdf6e3" + +[[themes]] +name = "catppuccin_latte" +[themes.palette] +background = "#eff1f5" +foreground = "#4c4f69" +black = "#5c5f77" +red = "#d20f39" +green = "#40a02b" +yellow = "#df8e1d" +blue = "#1e66f5" +magenta = "#ea76cb" +cyan = "#179299" +white = "#acb0be" +bright_black = "#6c6f85" +bright_red = "#de293e" +bright_green = "#49af3d" +bright_yellow = "#eea02d" +bright_blue = "#456eff" +bright_magenta = "#fe85d8" +bright_cyan = "#2d9fa8" +bright_white = "#bcc0cc" + +[[themes]] +name = "catppuccin_frappe" +[themes.palette] +background = "#303446" +foreground = "#c6d0f5" +black = "#51576d" +red = "#e78284" +green = "#a6d189" +yellow = "#e5c890" +blue = "#8caaee" +magenta = "#f4b8e4" +cyan = "#81c8be" +white = "#a5adce" +bright_black = "#626880" +bright_red = "#e67172" +bright_green = "#8ec772" +bright_yellow = "#d9ba73" +bright_blue = "#7b9ef0" +bright_magenta = "#f2a4db" +bright_cyan = "#5abfb5" +bright_white = "#b5bfe2" + +[[themes]] +name = "catppuccin_macchiato" +[themes.palette] +background = "#24273a" +foreground = "#cad3f5" +black = "#494d64" +red = "#ed8796" +green = "#a6da95" +yellow = "#eed49f" +blue = "#8aadf4" +magenta = "#f5bde6" +cyan = "#8bd5ca" +white = "#a5adcb" +bright_black = "#5b6078" +bright_red = "#ec7486" +bright_green = "#8ccf7f" +bright_yellow = "#e1c682" +bright_blue = "#78a1f6" +bright_magenta = "#f2a9dd" +bright_cyan = "#63cbc0" +bright_white = "#b8c0e0" + +[[themes]] +name = "catppuccin_mocha" +[themes.palette] +background = "#1e1e2e" +foreground = "#cdd6f4" +black = "#45475a" +red = "#f38ba8" +green = "#a6e3a1" +yellow = "#f9e2af" +blue = "#89b4fa" +magenta = "#f5c2e7" +cyan = "#94e2d5" +white = "#a6adc8" +bright_black = "#585b70" +bright_red = "#f37799" +bright_green = "#89d88b" +bright_yellow = "#ebd391" +bright_blue = "#74a8fc" +bright_magenta = "#f2aede" +bright_cyan = "#6bd7ca" +bright_white = "#bac2de" diff --git a/spotify_player/src/cli/client.rs b/spotify_player/src/cli/client.rs index b1cc0786..a2afa17e 100644 --- a/spotify_player/src/cli/client.rs +++ b/spotify_player/src/cli/client.rs @@ -388,6 +388,15 @@ async fn handle_playback_request( PlayerRequest::StartPlayback(Playback::Context(context_id, None), Some(shuffle)) } + Command::StartTrack(id_or_name) => { + let ItemId::Track(id) = get_spotify_id(client, ItemType::Track, id_or_name).await? + else { + anyhow::bail!("Unable to get track id") + }; + + let track = client.track(id).await?; + PlayerRequest::StartPlayback(Playback::URIs(vec![track.id.into()], None), None) + } Command::PlayPause => PlayerRequest::ResumePause, Command::Play => PlayerRequest::Resume, Command::Pause => PlayerRequest::Pause, diff --git a/spotify_player/src/cli/commands.rs b/spotify_player/src/cli/commands.rs index 14c71c85..2e5e6b4e 100644 --- a/spotify_player/src/cli/commands.rs +++ b/spotify_player/src/cli/commands.rs @@ -47,6 +47,9 @@ fn init_playback_start_subcommand() -> Command { .help("Shuffle tracks within the launched playback"), ), )) + .subcommand(add_id_or_name_group( + Command::new("track").about("Start playback for a track"), + )) .subcommand( Command::new("liked") .about("Start a liked tracks playback") diff --git a/spotify_player/src/cli/handlers.rs b/spotify_player/src/cli/handlers.rs index 3bb36984..11ec3480 100644 --- a/spotify_player/src/cli/handlers.rs +++ b/spotify_player/src/cli/handlers.rs @@ -75,6 +75,7 @@ fn handle_playback_subcommand(args: &ArgMatches) -> Result { let (cmd, args) = args.subcommand().expect("playback subcommand is required"); let command = match cmd { "start" => match args.subcommand() { + Some(("track", args)) => Command::StartTrack(get_id_or_name(args)), Some(("context", args)) => { let context_type = args .get_one::("context_type") diff --git a/spotify_player/src/cli/mod.rs b/spotify_player/src/cli/mod.rs index 06685584..3bee6516 100644 --- a/spotify_player/src/cli/mod.rs +++ b/spotify_player/src/cli/mod.rs @@ -91,6 +91,7 @@ pub enum Command { id_or_name: IdOrName, shuffle: bool, }, + StartTrack(IdOrName), StartLikedTracks { limit: usize, random: bool, diff --git a/spotify_player/src/config/mod.rs b/spotify_player/src/config/mod.rs index 93f403b5..8201c4a6 100644 --- a/spotify_player/src/config/mod.rs +++ b/spotify_player/src/config/mod.rs @@ -424,7 +424,7 @@ impl AppConfig { /// Returns stdout of `client_id_command` if set, otherwise it returns the the value of `client_id` pub fn get_client_id(&self) -> Result { match self.client_id_command { - Some(ref cmd) => cmd.execute(None), + Some(ref cmd) => cmd.execute(None).map(|out| out.trim().into()), None => Ok(self.client_id.clone()), } }