Add cargo-server with liby service draft

It is not smart, fetches everything all the time  and totally hardcoded with the tidal client
But tidal login and library discovery with tidal should somewhat work.
This commit is contained in:
Hans Mündelein 2023-05-22 22:05:38 +02:00
parent b5f722f1cb
commit 0af8829987
Signed by: hans
GPG Key ID: BA7B55E984CE74F4
7 changed files with 3596 additions and 58 deletions

328
Cargo.lock generated
View File

@ -46,6 +46,12 @@ dependencies = [
"syn 2.0.16",
]
[[package]]
name = "atomic_refcell"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31"
[[package]]
name = "autocfg"
version = "1.1.0"
@ -160,6 +166,16 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-expr"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -273,6 +289,24 @@ dependencies = [
"tonic-build",
]
[[package]]
name = "crabidy-server"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"crabidy-core",
"flume",
"gstreamer",
"gstreamer-play",
"once_cell",
"serde",
"serde_json",
"tidaldy",
"tokio",
"tonic",
]
[[package]]
name = "crossterm"
version = "0.26.1"
@ -504,6 +538,28 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-executor"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-macro"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.16",
]
[[package]]
name = "futures-sink"
version = "0.3.28"
@ -523,9 +579,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
@ -551,6 +609,78 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gio-sys"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1d43b0d7968b48455244ecafe41192871257f5740aa6b095eb19db78e362a5"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"winapi",
]
[[package]]
name = "glib"
version = "0.17.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f1de7cbde31ea4f0a919453a2dcece5d54d5b70e08f8ad254dc4840f5f09b6"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys",
"glib-macros",
"glib-sys",
"gobject-sys",
"libc",
"memchr",
"once_cell",
"smallvec",
"thiserror",
]
[[package]]
name = "glib-macros"
version = "0.17.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a7206c5c03851ef126ea1444990e81fdd6765fb799d5bc694e4897ca01bb97f"
dependencies = [
"anyhow",
"heck 0.4.1",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "glib-sys"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f00ad0a1bf548e61adfff15d83430941d9e1bb620e334f779edd1c745680a5"
dependencies = [
"libc",
"system-deps",
]
[[package]]
name = "gobject-sys"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e75b0000a64632b2d8ca3cf856af9308e3a970844f6e9659bd197f026793d0"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "graphql-parser"
version = "0.4.0"
@ -561,6 +691,131 @@ dependencies = [
"thiserror",
]
[[package]]
name = "gstreamer"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4530401c89be6dc10d77ae1587b811cf455c97dce7abf594cb9164527c7da7fc"
dependencies = [
"bitflags",
"cfg-if",
"futures-channel",
"futures-core",
"futures-util",
"glib",
"gstreamer-sys",
"libc",
"muldiv",
"num-integer",
"num-rational",
"once_cell",
"option-operations",
"paste",
"pretty-hex",
"smallvec",
"thiserror",
]
[[package]]
name = "gstreamer-base"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8ff5dfbf7bcaf1466a385b836bad0d8da25759f121458727fdda1f771c69b3"
dependencies = [
"atomic_refcell",
"bitflags",
"cfg-if",
"glib",
"gstreamer",
"gstreamer-base-sys",
"libc",
]
[[package]]
name = "gstreamer-base-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26114ed96f6668380f5a1554128159e98e06c3a7a8460f216d7cd6dce28f928c"
dependencies = [
"glib-sys",
"gobject-sys",
"gstreamer-sys",
"libc",
"system-deps",
]
[[package]]
name = "gstreamer-play"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f752a53171e330c7f56db24ca91d99b7958dc86395ebe91b117226d339b29306"
dependencies = [
"bitflags",
"glib",
"gstreamer",
"gstreamer-play-sys",
"gstreamer-video",
"libc",
"once_cell",
]
[[package]]
name = "gstreamer-play-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b69030bd53c3e5988a1e13bdb55ae8d922f8e9c2b522bfa2442bc13906829fb"
dependencies = [
"glib-sys",
"gobject-sys",
"gstreamer-sys",
"gstreamer-video-sys",
"libc",
"system-deps",
]
[[package]]
name = "gstreamer-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56fe047adef7d47dbafa8bc1340fddb53c325e16574763063702fc94b5786d2"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "gstreamer-video"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce97769effde2d779dc4f7037b37106457b74e53f2a711bddc90b30ffeb7e06"
dependencies = [
"bitflags",
"cfg-if",
"futures-channel",
"glib",
"gstreamer",
"gstreamer-base",
"gstreamer-video-sys",
"libc",
"once_cell",
]
[[package]]
name = "gstreamer-video-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66ddb6112d438aac0004d2db6053a572f92b1c5e0e9d6ff6c71d9245f7f73e46"
dependencies = [
"glib-sys",
"gobject-sys",
"gstreamer-base-sys",
"gstreamer-sys",
"libc",
"system-deps",
]
[[package]]
name = "h2"
version = "0.3.19"
@ -876,6 +1131,12 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "muldiv"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0"
[[package]]
name = "multimap"
version = "0.8.3"
@ -919,6 +1180,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
@ -988,6 +1260,15 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "option-operations"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0"
dependencies = [
"paste",
]
[[package]]
name = "ouroboros"
version = "0.15.6"
@ -1034,6 +1315,12 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "paste"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "percent-encoding"
version = "2.2.0"
@ -1138,6 +1425,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "pretty-hex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5"
[[package]]
name = "prettyplease"
version = "0.1.25"
@ -1148,6 +1441,16 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -1665,6 +1968,25 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "system-deps"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2"
dependencies = [
"cfg-expr",
"heck 0.4.1",
"pkg-config",
"toml 0.7.4",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5"
[[package]]
name = "tempfile"
version = "3.5.0"
@ -2055,6 +2377,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
[[package]]
name = "version_check"
version = "0.9.4"

View File

@ -1,2 +1,2 @@
[workspace]
members = ["crabidy-core", "crabidy", "cbd-tui", "tidaldy"]
members = ["cbd-tui", "crabidy-core", "crabidy-server", "crabidy", "tidaldy"]

2992
crabidy-server/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

20
crabidy-server/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "crabidy-server"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.71"
gstreamer = "0.20.5"
gstreamer-play = "0.20.2"
tokio = { version = "1.28.0", features = ["full"] }
tidaldy = { path = "../tidaldy" }
crabidy-core = { path = "../crabidy-core" }
once_cell = "1.17.1"
serde_json = "1.0.96"
serde = "1.0.163"
flume = "0.10.14"
tonic = "0.9.2"
async-trait = "0.1.68"

245
crabidy-server/src/main.rs Normal file
View File

@ -0,0 +1,245 @@
use anyhow::{Error, Result};
use async_trait::async_trait;
use crabidy_core::proto::crabidy::{
library_service_server::{LibraryService, LibraryServiceServer},
playback_server::{Playback, PlaybackServer},
queue_server::{Queue, QueueServer},
GetLibraryNodeRequest, GetLibraryNodeResponse, GetTrackRequest, GetTrackResponse, LibraryNode,
LibraryNodeState,
};
use crabidy_core::{ProviderClient, ProviderError};
use gstreamer_play::{Play, PlayMessage, PlayState, PlayVideoRenderer};
use once_cell::sync::OnceCell;
use std::{
collections::HashMap,
fs,
sync::{Arc, RwLock},
};
use tonic::{transport::Server, Request, Response, Status};
// static CHANNEL: OnceCell<flume::Sender<Input>> = OnceCell::new();
static ORCHESTRATOR_CHANNEL: OnceCell<flume::Sender<OrchestratorMessage>> = OnceCell::new();
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let orchestrator = ClientOrchestrator::init("").await.unwrap();
orchestrator.run();
let addr = "[::1]:50051".parse()?;
let crabidy_service = Library::new();
Server::builder()
.add_service(LibraryServiceServer::new(crabidy_service))
.serve(addr)
.await?;
Ok(())
}
enum OrchestratorMessage {
GetNode {
uuid: String,
callback: flume::Sender<LibraryNode>,
},
GetTracksPlaybackUrls {
uuid: String,
callback: flume::Sender<Vec<String>>,
},
}
#[derive(Debug)]
struct ClientOrchestrator {
rx: flume::Receiver<OrchestratorMessage>,
tidal_client: tidaldy::Client,
}
impl ClientOrchestrator {
fn run(self) {
tokio::spawn(async move {
while let Ok(msg) = self.rx.recv_async().await {
match msg {
OrchestratorMessage::GetNode { uuid, callback } => {
let node = match uuid.as_str() {
"/" => self.get_library_root(),
_ => self.get_library_node(&uuid).await.unwrap(),
};
callback.send_async(node).await;
}
OrchestratorMessage::GetTracksPlaybackUrls { uuid, callback } => {
let urls = self.get_urls_for_track(&uuid).await.unwrap();
callback.send_async(urls).await;
}
}
}
});
}
}
#[async_trait]
impl ProviderClient for ClientOrchestrator {
async fn init(_s: &str) -> Result<Self, ProviderError> {
let raw_toml_settings = fs::read_to_string("/tmp/tidaldy.toml").unwrap_or("".to_owned());
let tidal_client = tidaldy::Client::init(&raw_toml_settings).await.unwrap();
let new_toml_config = tidal_client.settings();
fs::write("/tmp/tidaldy.toml", new_toml_config).unwrap();
let (tx, rx) = flume::unbounded();
ORCHESTRATOR_CHANNEL.set(tx).unwrap();
Ok(Self { rx, tidal_client })
}
fn settings(&self) -> String {
"".to_owned()
}
async fn get_urls_for_track(&self, track_uuid: &str) -> Result<Vec<String>, ProviderError> {
self.tidal_client.get_urls_for_track(track_uuid).await
}
fn get_library_root(&self) -> LibraryNode {
let mut root_node = LibraryNode::new();
root_node.children.push("tidal".to_owned());
root_node
}
async fn get_library_node(&self, uuid: &str) -> Result<LibraryNode, ProviderError> {
if uuid == "tidal" {
return Ok(self.tidal_client.get_library_root());
}
self.tidal_client.get_library_node(uuid).await
}
}
#[derive(Debug)]
struct Library {
known_nodes: RwLock<HashMap<String, LibraryNode>>,
clients: Arc<HashMap<String, Box<dyn ProviderClient>>>,
}
impl Library {
fn new() -> Self {
Self {
known_nodes: RwLock::new(HashMap::new()),
clients: Arc::new(HashMap::new()),
}
}
}
#[tonic::async_trait]
impl LibraryService for Library {
async fn get_library_node(
&self,
request: Request<GetLibraryNodeRequest>,
) -> Result<Response<GetLibraryNodeResponse>, Status> {
println!("Got a library node request: {:?}", request);
let node_uuid = request.into_inner().uuid;
let (tx, rx) = flume::bounded(1);
ORCHESTRATOR_CHANNEL
.wait()
.send_async(OrchestratorMessage::GetNode {
uuid: node_uuid,
callback: tx,
})
.await
.unwrap();
let node = rx.recv_async().await.unwrap();
let resp = GetLibraryNodeResponse { node: Some(node) };
Ok(Response::new(resp))
}
async fn get_track(
&self,
request: Request<GetTrackRequest>,
) -> Result<Response<GetTrackResponse>, Status> {
println!("Got a track request: {:?}", request);
let req = request.into_inner();
let reply = GetTrackResponse { track: None };
Ok(Response::new(reply))
}
}
// #[derive(Debug)]
// enum Input {
// PlayTrack {
// track_id: String,
// },
// StopTrack {
// track_id: String,
// },
// GetTrack {
// track_id: String,
// response: tokio::sync::oneshot::Sender<tidaldy::Track>,
// },
// GetPlaylistList {
// response: tokio::sync::oneshot::Sender<Vec<tidaldy::PlaylistAndFavorite>>,
// },
// TrackOver,
// }
// async fn run() -> Result<(), Error> {
// gstreamer::init().unwrap();
// let play = Play::new(None::<PlayVideoRenderer>);
// let bus = play.message_bus();
// let (tx, rx) = flume::bounded(64);
// let bus_tx = tx.clone();
// bus.set_sync_handler(move |_, msg| {
// match PlayMessage::parse(msg) {
// Ok(PlayMessage::EndOfStream) => {}
// Ok(PlayMessage::StateChanged { state }) => {
// println!("State changed: {:?}", state);
// }
// Ok(PlayMessage::PositionUpdated { position }) => {
// println!("Position updated: {:?}", position);
// }
// _ => {}
// }
// gstreamer::BusSyncReply::Drop
// });
// let mut state = PlayState::Stopped;
// CHANNEL.set(tx).unwrap();
// while let Ok(input) = rx.recv_async().await {
// match (&mut state, input) {
// (_, Input::TrackOver) => {
// state = PlayState::Stopped;
// println!("Track stopped");
// }
// (_, Input::StopTrack { track_id }) => {
// println!("Stopping track {}", track_id);
// play.stop();
// state = PlayState::Stopped;
// }
// (_, Input::GetTrack { track_id, response }) => {
// let track = client.get_track(track_id).await.unwrap();
// response.send(track).unwrap();
// }
// (_, Input::GetPlaylistList { response }) => {
// println!("Getting playlists");
// let user_id = client.get_user_id().unwrap();
// println!("Getting playlists for user {}", user_id);
// let list = client
// .get_users_playlists_and_favorite_playlists(&user_id)
// .await
// .unwrap();
// response.send(list).unwrap();
// }
// (PlayState::Stopped, Input::PlayTrack { track_id }) => {
// println!("Playing track {}", track_id);
// let track_playback = client.get_track_playback(&track_id).await.unwrap();
// let manifest = track_playback.get_manifest().unwrap();
// play.set_uri(Some(&manifest.urls[0]));
// play.play();
// state = PlayState::Playing;
// }
// (PlayState::Paused, Input::PlayTrack { track_id }) => {
// println!("Unpausing track {}", track_id);
// play.play();
// state = PlayState::Playing;
// }
// (PlayState::Playing, Input::PlayTrack { track_id }) => {
// println!("Pausing track {}", track_id);
// play.pause();
// state = PlayState::Paused;
// }
// _ => {}
// }
// }
// print!("done");
// Ok(())
// }

View File

@ -81,7 +81,7 @@ impl crabidy_core::ProviderClient for Client {
let mut node = crabidy_core::proto::crabidy::LibraryNode {
uuid: "userplaylists".to_string(),
name: "playlists".to_string(),
parent: Some(format!("{}", global_root.uuid)),
parent: Some("tidal".to_string()),
state: crabidy_core::proto::crabidy::LibraryNodeState::Unspecified as i32,
tracks: Vec::new(),
children: Vec::new(),
@ -91,7 +91,8 @@ impl crabidy_core::ProviderClient for Client {
.get_users_playlists_and_favorite_playlists(&user_id)
.await?
{
node.children.push(playlist.playlist.uuid);
node.children
.push(format!("playlist:{}", playlist.playlist.uuid));
}
node
}
@ -117,8 +118,8 @@ impl crabidy_core::ProviderClient for Client {
fn split_uuid(uuid: &str) -> (String, String) {
let mut split = uuid.splitn(2, ':');
(
split.next().unwrap().to_string(),
split.next().unwrap().to_string(),
split.next().unwrap_or("").to_string(),
split.next().unwrap_or("").to_string(),
)
}
@ -360,10 +361,11 @@ impl Client {
pub async fn login_web(&mut self) -> Result<(), ClientError> {
let code_response = self.get_device_code().await?;
let now = Instant::now();
println!("{}", code_response.verification_uri_complete);
println!("https://{}", code_response.verification_uri_complete);
while now.elapsed().as_secs() <= code_response.expires_in {
let login = self.check_auth_status(&code_response.device_code).await;
if login.is_err() {
// println!("login failed with {:?}", login);
sleep(Duration::from_secs(code_response.interval)).await;
continue;
}
@ -374,7 +376,7 @@ impl Client {
self.settings.login.access_token = Some(login_results.access_token);
self.settings.login.refresh_token = login_results.refresh_token;
self.settings.login.expires_after = Some(login_results.expires_in + timestamp);
self.settings.login.user_id = Some(login_results.user.user_id);
self.settings.login.user_id = Some(login_results.user.user_id.to_string());
self.settings.login.country_code = Some(login_results.user.country_code);
return Ok(());
}
@ -493,6 +495,7 @@ impl Client {
.header("Content-Type", "application/x-www-form-urlencoded")
.send()
.await?;
// println!("{:#?} -> {}", res.status(), res.status().is_success());
if !res.status().is_success() {
if res.status().is_client_error() {
return Err(ClientError::AuthError(format!(

View File

@ -4,12 +4,6 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use thiserror::Error;
pub trait Paginated {
fn offset(&self) -> usize;
fn limit(&self) -> usize;
fn total(&self) -> usize;
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Page<T> {
@ -19,27 +13,6 @@ pub struct Page<T> {
pub items: Vec<T>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArtistSearchPage {
pub limit: i64,
pub offset: i64,
pub total_number_of_items: i64,
pub items: Vec<Item>,
}
impl Paginated for ArtistSearchPage {
fn offset(&self) -> usize {
self.offset as usize
}
fn limit(&self) -> usize {
self.limit as usize
}
fn total(&self) -> usize {
self.total_number_of_items as usize
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Item {
@ -127,7 +100,7 @@ pub struct RefreshResponse {
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct UserResponse {
pub user_id: String,
pub user_id: u64,
pub country_code: String,
}
@ -349,29 +322,6 @@ pub struct Creator {
pub id: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlaylistTracksPage {
pub limit: usize,
pub offset: usize,
pub total_number_of_items: usize,
pub items: Vec<PlaylistTrack>,
}
impl Paginated for PlaylistTracksPage {
fn offset(&self) -> usize {
self.offset
}
fn limit(&self) -> usize {
self.limit
}
fn total(&self) -> usize {
self.total_number_of_items
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlaylistTrack {