Skip to content

Commit

Permalink
Cache lyrics and add ability to skip to position in song via lyrics
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Oldham committed Oct 1, 2024
1 parent d2b54ff commit 57006a6
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 34 deletions.
1 change: 1 addition & 0 deletions psst-gui/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub const PLAY_STOP: Selector = Selector::new("app.play-stop");
pub const ADD_TO_QUEUE: Selector<(QueueEntry, PlaybackItem)> = Selector::new("app.add-to-queue");
pub const PLAY_QUEUE_BEHAVIOR: Selector<QueueBehavior> = Selector::new("app.play-queue-behavior");
pub const PLAY_SEEK: Selector<f64> = Selector::new("app.play-seek");
pub const SKIP_TO_POSITION: Selector<u64> = Selector::new("app.skip-to-position");

// Sorting control
pub const SORT_BY_DATE_ADDED: Selector = Selector::new("app.sort-by-date-added");
Expand Down
6 changes: 6 additions & 0 deletions psst-gui/src/controller/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,12 @@ where
}
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::SKIP_TO_POSITION) => {
let location = cmd.get_unchecked(cmd::SKIP_TO_POSITION);
self.seek(Duration::from_millis(location.clone()));

ctx.set_handled();
}
// Keyboard shortcuts.
Event::KeyDown(key) if key.code == Code::Space => {
self.pause_or_resume();
Expand Down
8 changes: 6 additions & 2 deletions psst-gui/src/ui/lyrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use druid::widget::{Flex, Label, LineBreaking};
use druid::Insets;
use druid::{widget::List, LensExt, Selector, Widget, WidgetExt};

use crate::cmd;
use crate::data::{Ctx, NowPlaying, TrackLines};
use crate::{
data::AppState,
Expand Down Expand Up @@ -31,8 +32,11 @@ fn user_top_tracks_widget() -> impl Widget<AppState> {
.expand_width()
.padding(Insets::uniform_xy(theme::grid(2.0), theme::grid(0.6)))
.link()
// 19360
.on_left_click(|ctx, _, c, _| ctx.submit_command(cmd::SKIP_TO_POSITION.with(c.data.start_time_ms.parse::<u64>().unwrap())))

})},
error_widget,
|| Label::new("No lyrics for this song!"),
)
.lens(
Ctx::make(
Expand All @@ -43,7 +47,7 @@ fn user_top_tracks_widget() -> impl Widget<AppState> {
)
.on_command_async(
LOAD_LYRICS,
|t| WebApi::global().get_lyrics(t.item.id().to_base62(), t.cover_image_url(250.0, 250.0).unwrap().to_string()),
|t| WebApi::global().get_lyrics(t.item.id().to_base62()),
|_, data, _| data.lyrics.defer(()),
|_, data, r| data.lyrics.update(((), r.1)),
)
Expand Down
39 changes: 7 additions & 32 deletions psst-gui/src/webapi/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,60 +820,35 @@ impl WebApi {
Ok(result)
}

pub fn get_lyrics(&self, track_id: String, image_url: String) -> Result<Vector<TrackLines>, Error> {
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
pub fn get_lyrics(&self, track_id: String) -> Result<Vector<TrackLines>, Error> {
#[derive(Default, Debug, Clone, PartialEq, Deserialize, Data)]
#[serde(rename_all = "camelCase")]
pub struct Root {
pub lyrics: Lyrics,
pub colors: Colors,
pub has_vocal_removal: bool,
}

#[derive(Default, Debug, Clone, PartialEq, Deserialize)]

#[derive(Default, Debug, Clone, PartialEq, Deserialize, Data)]
#[serde(rename_all = "camelCase")]
pub struct Lyrics {
pub sync_type: String,
pub lines: Vector<TrackLines>,
pub provider: String,
pub provider_lyrics_id: String,
pub provider_display_name: String,
pub sync_lyrics_uri: String,
pub is_dense_typeface: bool,
pub language: String,
pub is_rtl_language: bool,
pub show_upsell: bool,
pub cap_status: String,
pub is_snippet: bool,
}

#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Colors {
pub background: i64,
pub text: i64,
pub highlight_text: i64,
}
// https://spclient.wg.spotify.com/color-lyrics/v2/track/6h4yONyGIFXYhrvEX6jVeb/image/https%3A%2F%2Fi.scdn.co%2Fimage%2Fab67616d0000b273f8bd876cdda0e7a825bb9afb?format=json&vocalRemoval=false&market=from_token
let token = self.access_token()?;
let request = self
.agent
.request("GET", &format!("https://{}/{}", "spclient.wg.spotify.com", format!("color-lyrics/v2/track/{}/image/https%3A%2F%2Fi.scdn.co%2Fimage%2F{}", track_id, track_id.clone().split_off(3))))
.request("GET", &format!("https://spclient.wg.spotify.com/color-lyrics/v2/track/{}/image/https%3A%2F%2Fi.scdn.co%2Fimage%2F{}", track_id, track_id.clone().split_off(3)))
.query("format", "json")
.query("vocalRemoval", "false")
.query("market", "from_token")
// This is the reason for redoing the request method here rather than reusing as the order needs to be this way round.
.set("app-platform", "WebPlayer")
.set("Authorization", &format!("Bearer {}", &token));

// WE can defiantly cache this very effectivly!
match self.load::<Root>(request) {
Ok(result) => {
Ok(result.lyrics.lines)
},
Err(e) => {
Err(e)
}
}
let result: Cached<Root> = self.load_cached(request.clone(), "Lyrics", &track_id)?;
Ok(result.data.lyrics.lines)
}
}

Expand Down

0 comments on commit 57006a6

Please sign in to comment.