Skip to content

Commit ef6f78f

Browse files
authored
Support open spotify link (#307)
Resolves #218 This PR adds support for opening track/playlist/album/artist Spotify link from `spotify_player` by calling `OpenSpotifyLinkFromClipboard` command. The application will - open a corresponding context page for playlist/album/artist link - play the song for track link ## Changes - add `clipboard` crate as a dependency - add `OpenSpotifyLinkFromClipboard` command (default keymap: `O`)
1 parent 2358708 commit ef6f78f

File tree

8 files changed

+120
-13
lines changed

8 files changed

+120
-13
lines changed

.github/workflows/cd.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ jobs:
2222
- os: ubuntu-latest
2323
target: x86_64-unknown-linux-gnu
2424
container: rust
25-
dependencies: "libssl-dev libasound2-dev libdbus-1-dev"
25+
dependencies: "libssl-dev libasound2-dev libdbus-1-dev libxcb-shape0-dev libxcb-xfixes0-dev"
2626
- os: ubuntu-latest
2727
target: aarch64-unknown-linux-gnu
2828
container: rustembedded/cross:aarch64-unknown-linux-gnu
2929
cross_arch: "arm64"
3030
pkg_config_path: "/usr/lib/aarch64-linux-gnu/pkgconfig/"
31-
dependencies: "libssl-dev:arm64 libasound2-dev:arm64 libdbus-1-dev:arm64"
31+
dependencies: "libssl-dev:arm64 libasound2-dev:arm64 libdbus-1-dev:arm64 libxcb-shape0-dev:arm64 libxcb-xfixes0-dev:arm64"
3232
- os: ubuntu-latest
3333
target: armv7-unknown-linux-gnueabihf
3434
cross_arch: "armhf"
3535
pkg_config_path: "/usr/lib/arm-linux-gnueabihf/pkgconfig/"
3636
container: rustembedded/cross:armv7-unknown-linux-gnueabihf
37-
dependencies: "libssl-dev:armhf libasound2-dev:armhf libdbus-1-dev:armhf"
37+
dependencies: "libssl-dev:armhf libasound2-dev:armhf libdbus-1-dev:armhf libxcb-shape0-dev:armhf libxcb-xfixes0-dev:armhf"
3838
- os: windows-latest
3939
target: x86_64-pc-windows-msvc
4040

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323

2424
steps:
2525
- name: Install dependencies
26-
run: sudo apt-get update && sudo apt-get install libssl-dev libasound2-dev libdbus-1-dev
26+
run: sudo apt-get update && sudo apt-get install libssl-dev libasound2-dev libdbus-1-dev libxcb-shape0-dev libxcb-xfixes0-dev
2727
if: ${{ runner.os == 'Linux' }}
2828

2929
- name: Checkout source

Cargo.lock

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,10 @@ A Spotify Premium account is **required**.
9393
##### Linux
9494

9595
- [Rust and cargo](https://www.rust-lang.org/tools/install) as the build dependencies
96-
- `openssl`, `alsa-lib` (`streaming` feature), `libdbus` (`media-control` feature) system libraries.
97-
- On Debian based systems, run the below command to install application's dependencies:
96+
- install `openssl`, `alsa-lib` (`streaming` feature), `libdbus` (`media-control` feature), `libxcb` system libraries.
97+
- For example, on Debian based systems, run the below command to install application's dependencies:
9898
```shell
99-
sudo apt install libssl-dev libasound2-dev libdbus-1-dev
100-
```
101-
- On Fedora based systems, run the below command to install application's dependencies:
102-
```shell
103-
sudo yum install openssl-devel alsa-lib-devel dbus-devel
99+
sudo apt install libssl-dev libasound2-dev libdbus-1-dev libxcb-shape0-dev libxcb-xfixes0-dev
104100
```
105101

106102
### Binaries
@@ -382,11 +378,12 @@ List of supported commands:
382378
| `SearchPage` | go to the search page | `g s` |
383379
| `BrowsePage` | go to the browse page | `g b` |
384380
| `PreviousPage` | go to the previous page | `backspace`, `C-q` |
381+
| `OpenSpotifyLinkFromClipboard` | open a Spotify link from clipboard | `O` |
385382
| `SortTrackByTitle` | sort the track table (if any) by track's title | `s t` |
386383
| `SortTrackByArtists` | sort the track table (if any) by track's artists | `s a` |
387384
| `SortTrackByAlbum` | sort the track table (if any) by track's album | `s A` |
388-
| `SortTrackByDuration` | sort the track table (if any) by track's duration | `s d` |
389385
| `SortTrackByAddedDate` | sort the track table (if any) by track's added date | `s D` |
386+
| `SortTrackByDuration` | sort the track table (if any) by track's duration | `s d` |
390387
| `ReverseOrder` | reverse the order of the track table (if any) | `s r` |
391388
| `MovePlaylistItemUp` | move playlist item up one position | `C-k` |
392389
| `MovePlaylistItemDown` | move playlist item down one position | `C-j` |

spotify_player/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ once_cell = "1.18.0"
4646
regex = "1.9.6"
4747
daemonize = { version = "0.5.0", optional = true }
4848
ttl_cache = "0.5.1"
49+
clipboard = "0.5.0"
4950

5051
[target.'cfg(target_os = "windows")'.dependencies.windows]
5152
version = "0.44"

spotify_player/src/command.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub enum Command {
6262
SearchPage,
6363
BrowsePage,
6464
PreviousPage,
65+
OpenSpotifyLinkFromClipboard,
6566

6667
SortTrackByTitle,
6768
SortTrackByArtists,
@@ -198,6 +199,7 @@ impl Command {
198199
Self::SearchPage => "go to the search page",
199200
Self::BrowsePage => "go to the browse page",
200201
Self::PreviousPage => "go to the previous page",
202+
Self::OpenSpotifyLinkFromClipboard => "open a Spotify link from clipboard",
201203
Self::SortTrackByTitle => "sort the track table (if any) by track's title",
202204
Self::SortTrackByArtists => "sort the track table (if any) by track's artists",
203205
Self::SortTrackByAlbum => "sort the track table (if any) by track's album",

spotify_player/src/config/keymap.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ impl Default for KeymapConfig {
182182
key_sequence: "C-q".into(),
183183
command: Command::PreviousPage,
184184
},
185+
Keymap {
186+
key_sequence: "O".into(),
187+
command: Command::OpenSpotifyLinkFromClipboard,
188+
},
185189
Keymap {
186190
key_sequence: "?".into(),
187191
command: Command::OpenCommandHelp,

spotify_player/src/event/mod.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use crate::{
77

88
#[cfg(feature = "lyric-finder")]
99
use crate::utils::map_join;
10-
use anyhow::Result;
10+
use anyhow::{Context as _, Result};
11+
use clipboard::{ClipboardContext, ClipboardProvider};
1112

1213
mod page;
1314
mod popup;
@@ -359,6 +360,54 @@ fn handle_global_command(
359360
ui.popup = None;
360361
}
361362
}
363+
Command::OpenSpotifyLinkFromClipboard => {
364+
let content = get_clipboard_content().context("get clipboard's content")?;
365+
let re = regex::Regex::new(
366+
r"https://open.spotify.com/(?P<type>.*?)/(?P<id>[[:alnum:]]*).*",
367+
)?;
368+
if let Some(cap) = re.captures(&content) {
369+
let typ = cap.name("type").expect("valid capture").as_str();
370+
let id = cap.name("id").expect("valid capture").as_str();
371+
match typ {
372+
// for track link, play the song
373+
"track" => {
374+
let id = TrackId::from_id(id)?.into_static();
375+
client_pub.send(ClientRequest::Player(PlayerRequest::StartPlayback(
376+
Playback::URIs(vec![id], None),
377+
None,
378+
)))?;
379+
}
380+
// for playlist/artist/album link, go to the corresponding context page
381+
"playlist" => {
382+
let id = PlaylistId::from_id(id)?.into_static();
383+
ui.create_new_page(PageState::Context {
384+
id: None,
385+
context_page_type: ContextPageType::Browsing(ContextId::Playlist(id)),
386+
state: None,
387+
});
388+
}
389+
"artist" => {
390+
let id = ArtistId::from_id(id)?.into_static();
391+
ui.create_new_page(PageState::Context {
392+
id: None,
393+
context_page_type: ContextPageType::Browsing(ContextId::Artist(id)),
394+
state: None,
395+
});
396+
}
397+
"album" => {
398+
let id = AlbumId::from_id(id)?.into_static();
399+
ui.create_new_page(PageState::Context {
400+
id: None,
401+
context_page_type: ContextPageType::Browsing(ContextId::Album(id)),
402+
state: None,
403+
});
404+
}
405+
e => anyhow::bail!("unsupported Spotify type {e}!"),
406+
}
407+
} else {
408+
tracing::warn!("clipboard's content ({content}) is not a valid Spotify link!");
409+
}
410+
}
362411
#[cfg(feature = "lyric-finder")]
363412
Command::LyricPage => {
364413
if let Some(track) = state.player.read().current_playing_track() {
@@ -412,3 +461,15 @@ fn handle_global_command(
412461
}
413462
Ok(true)
414463
}
464+
465+
fn get_clipboard_content() -> Result<String> {
466+
let mut clipboard_ctx: ClipboardContext = match ClipboardProvider::new() {
467+
Ok(ctx) => ctx,
468+
Err(err) => anyhow::bail!("{err:#}"),
469+
};
470+
let content = match clipboard_ctx.get_contents() {
471+
Ok(content) => content,
472+
Err(err) => anyhow::bail!("{err:#}"),
473+
};
474+
Ok(content)
475+
}

0 commit comments

Comments
 (0)