mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Initial C API
This commit is contained in:
parent
327bb8477e
commit
ae38e60518
8 changed files with 394 additions and 0 deletions
16
capi/Cargo.toml
Normal file
16
capi/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "librespot_capi"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Paul Liétar <paul@lietar.net>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "librespot_capi"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
eventual = "~0.1.5"
|
||||||
|
owning_ref = "0.1.*"
|
||||||
|
|
||||||
|
[dependencies.librespot]
|
||||||
|
path = "../"
|
33
capi/src/artist.rs
Normal file
33
capi/src/artist.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use libc::c_char;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use librespot::metadata::Artist;
|
||||||
|
|
||||||
|
use metadata::SpMetadata;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type sp_artist = SpMetadata<Artist>;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_artist_is_loaded(c_artist: *mut sp_artist) -> bool {
|
||||||
|
let artist = &*c_artist;
|
||||||
|
artist.is_loaded()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_artist_name(c_artist: *mut sp_artist) -> *const c_char {
|
||||||
|
let artist = &*c_artist;
|
||||||
|
|
||||||
|
let name = artist.get()
|
||||||
|
.map(|metadata| metadata.name.clone())
|
||||||
|
.unwrap_or("".to_owned());
|
||||||
|
|
||||||
|
let name = CString::new(name).unwrap();
|
||||||
|
let c_name = name.as_ptr();
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
mem::forget(name);
|
||||||
|
|
||||||
|
c_name
|
||||||
|
}
|
17
capi/src/lib.rs
Normal file
17
capi/src/lib.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
extern crate librespot;
|
||||||
|
extern crate libc;
|
||||||
|
extern crate eventual;
|
||||||
|
extern crate owning_ref;
|
||||||
|
|
||||||
|
pub mod artist;
|
||||||
|
pub mod link;
|
||||||
|
pub mod metadata;
|
||||||
|
pub mod session;
|
||||||
|
pub mod track;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use types::sp_session_config;
|
||||||
|
pub use types::sp_error;
|
||||||
|
pub use types::sp_error::*;
|
||||||
|
|
||||||
|
|
36
capi/src/link.rs
Normal file
36
capi/src/link.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use metadata::SpMetadata;
|
||||||
|
use session::global_session;
|
||||||
|
use track::sp_track;
|
||||||
|
use types::sp_error;
|
||||||
|
use types::sp_error::*;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use libc::c_char;
|
||||||
|
use librespot::link::Link;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type sp_link = Rc<Link>;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_link_create_from_string(uri: *const c_char) -> *mut sp_link {
|
||||||
|
let uri = CStr::from_ptr(uri).to_string_lossy();
|
||||||
|
let link = Link::from_str(&uri).unwrap();
|
||||||
|
|
||||||
|
Box::into_raw(Box::new(Rc::new(link)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_link_release(c_link: *mut sp_link) -> sp_error {
|
||||||
|
drop(Box::from_raw(c_link));
|
||||||
|
|
||||||
|
SP_ERROR_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_link_as_track(c_link: *mut sp_link) -> *mut sp_track {
|
||||||
|
let link = &*c_link;
|
||||||
|
let session = &*global_session.unwrap();
|
||||||
|
|
||||||
|
let track = SpMetadata::from_future(link.as_track(session).unwrap());
|
||||||
|
Box::into_raw(Box::new(track))
|
||||||
|
}
|
54
capi/src/metadata.rs
Normal file
54
capi/src/metadata.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use eventual::Async;
|
||||||
|
use owning_ref::MutexGuardRef;
|
||||||
|
use std::sync::{Mutex, Arc};
|
||||||
|
|
||||||
|
use librespot::metadata::{MetadataTrait, MetadataRef};
|
||||||
|
|
||||||
|
pub enum SpMetadataInner<T: MetadataTrait> {
|
||||||
|
Loading,
|
||||||
|
Error,
|
||||||
|
Loaded(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SpMetadata<T: MetadataTrait>(Arc<Mutex<SpMetadataInner<T>>>);
|
||||||
|
|
||||||
|
impl <T: MetadataTrait> SpMetadata<T> {
|
||||||
|
pub fn from_future(future: MetadataRef<T>) -> SpMetadata<T> {
|
||||||
|
let metadata = Arc::new(Mutex::new(SpMetadataInner::Loading));
|
||||||
|
|
||||||
|
{
|
||||||
|
let metadata = metadata.clone();
|
||||||
|
future.receive(move |result| {
|
||||||
|
//let metadata = metadata.upgrade().unwrap();
|
||||||
|
let mut metadata = metadata.lock().unwrap();
|
||||||
|
|
||||||
|
*metadata = match result {
|
||||||
|
Ok(data) => SpMetadataInner::Loaded(data),
|
||||||
|
Err(_) => SpMetadataInner::Error,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SpMetadata(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_loaded(&self) -> bool {
|
||||||
|
self.get().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> Option<MutexGuardRef<SpMetadataInner<T>, T>> {
|
||||||
|
let inner = self.0.lock().unwrap();
|
||||||
|
|
||||||
|
match *inner {
|
||||||
|
SpMetadataInner::Loaded(_) => {
|
||||||
|
Some(MutexGuardRef::new(inner).map(|inner| {
|
||||||
|
match *inner {
|
||||||
|
SpMetadataInner::Loaded(ref metadata) => metadata,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
110
capi/src/session.rs
Normal file
110
capi/src/session.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use libc::{c_int, c_char};
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::mem;
|
||||||
|
use std::slice::from_raw_parts;
|
||||||
|
|
||||||
|
use librespot::session::{Session, Config, Bitrate};
|
||||||
|
|
||||||
|
use types::sp_error;
|
||||||
|
use types::sp_error::*;
|
||||||
|
use types::sp_session_config;
|
||||||
|
|
||||||
|
pub static mut global_session: Option<*mut Session> = None;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type sp_session = Session;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_session_create(c_config: *const sp_session_config,
|
||||||
|
c_session: *mut *mut sp_session) -> sp_error {
|
||||||
|
assert_eq!(global_session, None);
|
||||||
|
|
||||||
|
let c_config = &*c_config;
|
||||||
|
|
||||||
|
let application_key = from_raw_parts::<u8>(c_config.application_key as *const u8,
|
||||||
|
c_config.application_key_size);
|
||||||
|
|
||||||
|
let user_agent = CStr::from_ptr(c_config.user_agent).to_string_lossy().into_owned();
|
||||||
|
let device_name = CStr::from_ptr(c_config.device_id).to_string_lossy().into_owned();
|
||||||
|
let cache_location = CStr::from_ptr(c_config.cache_location).to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
application_key: application_key.to_owned(),
|
||||||
|
user_agent: user_agent,
|
||||||
|
device_name: device_name,
|
||||||
|
cache_location: cache_location.into(),
|
||||||
|
bitrate: Bitrate::Bitrate160,
|
||||||
|
};
|
||||||
|
|
||||||
|
let session = Box::new(Session::new(config));
|
||||||
|
let session = Box::into_raw(session);
|
||||||
|
|
||||||
|
global_session = Some(session);
|
||||||
|
*c_session = session;
|
||||||
|
|
||||||
|
SP_ERROR_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_session_release(c_session: *mut sp_session) -> sp_error {
|
||||||
|
assert_eq!(global_session, Some(c_session));
|
||||||
|
|
||||||
|
global_session = None;
|
||||||
|
drop(Box::from_raw(c_session));
|
||||||
|
|
||||||
|
SP_ERROR_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_session_login(c_session: *mut sp_session,
|
||||||
|
c_username: *const c_char,
|
||||||
|
c_password: *const c_char,
|
||||||
|
_remember_me: bool,
|
||||||
|
_blob: *const c_char) -> sp_error {
|
||||||
|
assert_eq!(global_session, Some(c_session));
|
||||||
|
|
||||||
|
let session = &*c_session;
|
||||||
|
|
||||||
|
let username = CStr::from_ptr(c_username).to_string_lossy().into_owned();
|
||||||
|
let password = CStr::from_ptr(c_password).to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
session.login_password(username, password).unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let session = session.clone();
|
||||||
|
::std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
session.poll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SP_ERROR_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_session_user_name(c_session: *mut sp_session) -> *const c_char {
|
||||||
|
assert_eq!(global_session, Some(c_session));
|
||||||
|
|
||||||
|
let session = &*c_session;
|
||||||
|
|
||||||
|
let username = CString::new(session.username()).unwrap();
|
||||||
|
let c_username = username.as_ptr();
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
mem::forget(username);
|
||||||
|
|
||||||
|
c_username
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_session_user_country(c_session: *mut sp_session) -> c_int {
|
||||||
|
assert_eq!(global_session, Some(c_session));
|
||||||
|
|
||||||
|
let session = &*c_session;
|
||||||
|
|
||||||
|
let country = session.username();
|
||||||
|
country.chars().fold(0, |acc, x| {
|
||||||
|
acc << 8 | (x as u32)
|
||||||
|
}) as c_int
|
||||||
|
}
|
60
capi/src/track.rs
Normal file
60
capi/src/track.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use libc::{c_int, c_char};
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
|
use artist::sp_artist;
|
||||||
|
use metadata::SpMetadata;
|
||||||
|
use session::global_session;
|
||||||
|
|
||||||
|
use librespot::metadata::{Track, Artist};
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type sp_track = SpMetadata<Track>;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_track_is_loaded(c_track: *mut sp_track) -> bool {
|
||||||
|
let track = &*c_track;
|
||||||
|
track.is_loaded()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_track_name(c_track: *mut sp_track) -> *const c_char {
|
||||||
|
let track = &*c_track;
|
||||||
|
|
||||||
|
let name = track.get()
|
||||||
|
.map(|metadata| metadata.name.clone())
|
||||||
|
.unwrap_or("".to_owned());
|
||||||
|
|
||||||
|
let name = CString::new(name).unwrap();
|
||||||
|
let c_name = name.as_ptr();
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
mem::forget(name);
|
||||||
|
|
||||||
|
c_name
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_track_num_artists(c_track: *mut sp_track) -> c_int {
|
||||||
|
let track = &*c_track;
|
||||||
|
|
||||||
|
track.get()
|
||||||
|
.map(|metadata| metadata.artists.len() as c_int)
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_track_artist(c_track: *mut sp_track, index: c_int) -> *mut sp_artist {
|
||||||
|
let track = &*c_track;
|
||||||
|
let session = &*global_session.unwrap();
|
||||||
|
|
||||||
|
track.get()
|
||||||
|
.and_then(|metadata| metadata.artists.get(index as usize).map(|x| *x))
|
||||||
|
.map(|artist_id| {
|
||||||
|
let artist = SpMetadata::from_future(session.metadata::<Artist>(artist_id));
|
||||||
|
Box::into_raw(Box::new(artist))
|
||||||
|
})
|
||||||
|
.unwrap_or(null_mut())
|
||||||
|
}
|
||||||
|
|
68
capi/src/types.rs
Normal file
68
capi/src/types.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use libc::size_t;
|
||||||
|
|
||||||
|
pub enum sp_session_callbacks {}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum sp_error {
|
||||||
|
SP_ERROR_OK = 0,
|
||||||
|
SP_ERROR_BAD_API_VERSION = 1,
|
||||||
|
SP_ERROR_API_INITIALIZATION_FAILED = 2,
|
||||||
|
SP_ERROR_TRACK_NOT_PLAYABLE = 3,
|
||||||
|
SP_ERROR_BAD_APPLICATION_KEY = 5,
|
||||||
|
SP_ERROR_BAD_USERNAME_OR_PASSWORD = 6,
|
||||||
|
SP_ERROR_USER_BANNED = 7,
|
||||||
|
SP_ERROR_UNABLE_TO_CONTACT_SERVER = 8,
|
||||||
|
SP_ERROR_CLIENT_TOO_OLD = 9,
|
||||||
|
SP_ERROR_OTHER_PERMANENT = 10,
|
||||||
|
SP_ERROR_BAD_USER_AGENT = 11,
|
||||||
|
SP_ERROR_MISSING_CALLBACK = 12,
|
||||||
|
SP_ERROR_INVALID_INDATA = 13,
|
||||||
|
SP_ERROR_INDEX_OUT_OF_RANGE = 14,
|
||||||
|
SP_ERROR_USER_NEEDS_PREMIUM = 15,
|
||||||
|
SP_ERROR_OTHER_TRANSIENT = 16,
|
||||||
|
SP_ERROR_IS_LOADING = 17,
|
||||||
|
SP_ERROR_NO_STREAM_AVAILABLE = 18,
|
||||||
|
SP_ERROR_PERMISSION_DENIED = 19,
|
||||||
|
SP_ERROR_INBOX_IS_FULL = 20,
|
||||||
|
SP_ERROR_NO_CACHE = 21,
|
||||||
|
SP_ERROR_NO_SUCH_USER = 22,
|
||||||
|
SP_ERROR_NO_CREDENTIALS = 23,
|
||||||
|
SP_ERROR_NETWORK_DISABLED = 24,
|
||||||
|
SP_ERROR_INVALID_DEVICE_ID = 25,
|
||||||
|
SP_ERROR_CANT_OPEN_TRACE_FILE = 26,
|
||||||
|
SP_ERROR_APPLICATION_BANNED = 27,
|
||||||
|
SP_ERROR_OFFLINE_TOO_MANY_TRACKS = 31,
|
||||||
|
SP_ERROR_OFFLINE_DISK_CACHE = 32,
|
||||||
|
SP_ERROR_OFFLINE_EXPIRED = 33,
|
||||||
|
SP_ERROR_OFFLINE_NOT_ALLOWED = 34,
|
||||||
|
SP_ERROR_OFFLINE_LICENSE_LOST = 35,
|
||||||
|
SP_ERROR_OFFLINE_LICENSE_ERROR = 36,
|
||||||
|
SP_ERROR_LASTFM_AUTH_ERROR = 39,
|
||||||
|
SP_ERROR_INVALID_ARGUMENT = 40,
|
||||||
|
SP_ERROR_SYSTEM_FAILURE = 41,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy,Clone)]
|
||||||
|
pub struct sp_session_config {
|
||||||
|
pub api_version: ::std::os::raw::c_int,
|
||||||
|
pub cache_location: *const ::std::os::raw::c_char,
|
||||||
|
pub settings_location: *const ::std::os::raw::c_char,
|
||||||
|
pub application_key: *const ::std::os::raw::c_void,
|
||||||
|
pub application_key_size: size_t,
|
||||||
|
pub user_agent: *const ::std::os::raw::c_char,
|
||||||
|
pub callbacks: *const sp_session_callbacks,
|
||||||
|
pub userdata: *mut ::std::os::raw::c_void,
|
||||||
|
pub compress_playlists: bool,
|
||||||
|
pub dont_save_metadata_for_playlists: bool,
|
||||||
|
pub initially_unload_playlists: bool,
|
||||||
|
pub device_id: *const ::std::os::raw::c_char,
|
||||||
|
pub proxy: *const ::std::os::raw::c_char,
|
||||||
|
pub proxy_username: *const ::std::os::raw::c_char,
|
||||||
|
pub proxy_password: *const ::std::os::raw::c_char,
|
||||||
|
pub tracefile: *const ::std::os::raw::c_char,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue