metadata: add lyrics

Save users from doing all the parsing themselves.
This commit is contained in:
Guillaume Desmottes 2022-12-26 18:03:27 +01:00
parent edf646d4bb
commit f72048e5e1
5 changed files with 84 additions and 0 deletions

View file

@ -94,6 +94,7 @@ https://github.com/librespot-org/librespot
Connect should use the 'filter-explicit-content' user attribute in the session.
- [playback] Add metadata support via a `TrackChanged` event
- [connect] Add `activate` and `load` functions to `Spirc`, allowing control over local connect sessions
- [metadata] Add `Lyrics`
### Fixed

2
Cargo.lock generated
View file

@ -1500,6 +1500,8 @@ dependencies = [
"librespot-protocol",
"log",
"protobuf",
"serde",
"serde_json",
"thiserror",
"uuid",
]

View file

@ -16,6 +16,8 @@ log = "0.4"
protobuf = "2"
thiserror = "1"
uuid = { version = "1", default-features = false }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dependencies.librespot-core]
path = "../core"

View file

@ -18,6 +18,7 @@ pub mod episode;
pub mod error;
pub mod external_id;
pub mod image;
pub mod lyrics;
pub mod playlist;
mod request;
pub mod restriction;
@ -33,6 +34,7 @@ use request::RequestResult;
pub use album::Album;
pub use artist::Artist;
pub use episode::Episode;
pub use lyrics::Lyrics;
pub use playlist::Playlist;
pub use show::Show;
pub use track::Track;

77
metadata/src/lyrics.rs Normal file
View file

@ -0,0 +1,77 @@
use bytes::Bytes;
use librespot_core::{Error, FileId, Session, SpotifyId};
impl Lyrics {
pub async fn get(session: &Session, id: &SpotifyId) -> Result<Self, Error> {
let spclient = session.spclient();
let lyrics = spclient.get_lyrics(id).await?;
Self::try_from(&lyrics)
}
pub async fn get_for_image(
session: &Session,
id: &SpotifyId,
image_id: &FileId,
) -> Result<Self, Error> {
let spclient = session.spclient();
let lyrics = spclient.get_lyrics_for_image(id, image_id).await?;
Self::try_from(&lyrics)
}
}
impl TryFrom<&Bytes> for Lyrics {
type Error = Error;
fn try_from(lyrics: &Bytes) -> Result<Self, Self::Error> {
serde_json::from_slice(lyrics).map_err(|err| err.into())
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Lyrics {
pub colors: Colors,
pub has_vocal_removal: bool,
pub lyrics: LyricsInner,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Colors {
pub background: i32,
pub highlight_text: i32,
pub text: i32,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LyricsInner {
// TODO: 'alternatives' field as an array but I don't know what it's meant for
pub fullscreen_action: String,
pub is_dense_typeface: bool,
pub is_rtl_language: bool,
pub language: String,
pub lines: Vec<Line>,
pub provider: String,
pub provider_display_name: String,
pub provider_lyrics_id: String,
pub sync_lyrics_uri: String,
pub sync_type: SyncType,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum SyncType {
Unsynced,
LineSynced,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Line {
pub start_time_ms: String,
pub end_time_ms: String,
pub words: String,
// TODO: 'syllables' array
}