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