Remove capi.

This was just some unsuccessful attempt at create an API compatible with
libspotify.

One day I’ll make a C API without caring about that compatibility
This commit is contained in:
Paul Lietar 2016-07-06 00:21:05 +01:00
parent 4d277e5b75
commit f207d53943
9 changed files with 0 additions and 564 deletions

View file

@ -1,15 +0,0 @@
[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"
[dependencies.librespot]
path = "../"

View file

@ -1,25 +0,0 @@
use libc::c_char;
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 = &mut *c_artist;
let name = artist.get()
.map(|metadata| &metadata.name as &str)
.unwrap_or("");
artist.intern(name).as_ptr()
}

View file

@ -1,21 +0,0 @@
use std::collections::HashMap;
use std::ffi::{CString, CStr};
pub struct CStringCache {
cache: HashMap<String, CString>
}
impl CStringCache {
pub fn new() -> CStringCache {
CStringCache {
cache: HashMap::new()
}
}
pub fn intern(&mut self, string: &str) -> &CStr {
self.cache.entry(string.to_owned()).or_insert_with(|| {
CString::new(string).unwrap()
})
}
}

View file

@ -1,14 +0,0 @@
#![feature(fnbox)]
extern crate librespot;
extern crate libc;
extern crate eventual;
pub mod artist;
pub mod link;
pub mod metadata;
pub mod session;
pub mod track;
mod types;
mod cstring_cache;

View file

@ -1,36 +0,0 @@
use metadata::SpMetadata;
use session::SpSession;
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 = SpSession::global();
let track = SpMetadata::from_future(link.as_track(&session.session).unwrap());
Box::into_raw(Box::new(track))
}

View file

@ -1,87 +0,0 @@
use eventual::Async;
use std::sync::Arc;
use std::ffi::CStr;
use std::cell::UnsafeCell;
use librespot::metadata::{MetadataTrait, MetadataRef};
use cstring_cache::CStringCache;
use session::SpSession;
pub struct UnsafeSyncCell<T> {
cell: UnsafeCell<T>
}
impl <T> UnsafeSyncCell<T> {
fn new(value: T) -> UnsafeSyncCell<T> {
UnsafeSyncCell { cell: UnsafeCell::new(value) }
}
fn get(&self) -> *mut T {
self.cell.get()
}
}
unsafe impl<T> Sync for UnsafeSyncCell<T> {}
pub enum SpMetadataState<T: MetadataTrait> {
Loading,
Error,
Loaded(T),
}
pub struct SpMetadata<T: MetadataTrait> {
state: Arc<UnsafeSyncCell<SpMetadataState<T>>>,
cache: CStringCache,
}
impl <T: MetadataTrait> SpMetadata<T> {
pub fn from_future(future: MetadataRef<T>) -> SpMetadata<T> {
let state = Arc::new(UnsafeSyncCell::new(SpMetadataState::Loading));
{
let state = state.clone();
SpSession::receive(future, move |session, result| {
let state = unsafe {
&mut *state.get()
};
*state = match result {
Ok(data) => SpMetadataState::Loaded(data),
Err(_) => SpMetadataState::Error,
};
unsafe {
if let Some(f) = session.callbacks.metadata_updated {
f(session as *mut _)
}
}
});
}
SpMetadata {
state: state,
cache: CStringCache::new(),
}
}
pub fn is_loaded(&self) -> bool {
unsafe {
self.get().is_some()
}
}
pub unsafe fn get(&self) -> Option<&'static T> {
let state = &*self.state.get();
match *state {
SpMetadataState::Loaded(ref metadata) => Some(metadata),
_ => None,
}
}
pub fn intern(&mut self, string: &str) -> &CStr {
self.cache.intern(string)
}
}

View file

@ -1,168 +0,0 @@
use libc::{c_int, c_char};
use std::ffi::CStr;
use std::slice::from_raw_parts;
use std::sync::mpsc;
use std::boxed::FnBox;
use std::sync::Mutex;
use librespot::session::{Session, Config, Bitrate};
use eventual::{Async, AsyncResult, Future};
use cstring_cache::CStringCache;
use types::sp_error;
use types::sp_error::*;
use types::sp_session_config;
use types::sp_session_callbacks;
static mut global_session: Option<(*const sp_session, *const Mutex<mpsc::Sender<SpSessionEvent>>)> = None;
pub type SpSessionEvent = Box<FnBox(&mut SpSession) -> ()>;
pub struct SpSession {
pub session: Session,
cache: CStringCache,
rx: mpsc::Receiver<SpSessionEvent>,
pub callbacks: &'static sp_session_callbacks,
}
impl SpSession {
pub unsafe fn global() -> &'static SpSession {
&*global_session.unwrap().0
}
pub fn run<F: FnOnce(&mut SpSession) -> () + 'static>(event: F) {
let tx = unsafe {
&*global_session.unwrap().1
};
tx.lock().unwrap().send(Box::new(event)).unwrap();
}
pub fn receive<T, E, F>(future: Future<T, E>, handler: F)
where T : Send, E: Send,
F : FnOnce(&mut SpSession, AsyncResult<T, E>) -> () + Send + 'static {
future.receive(move |result| {
SpSession::run(move |session| {
handler(session, result);
})
})
}
}
#[allow(non_camel_case_types)]
pub type sp_session = SpSession;
#[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!(global_session.is_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 (tx, rx) = mpsc::channel();
let session = SpSession {
session: Session::new(config),
cache: CStringCache::new(),
rx: rx,
callbacks: &*c_config.callbacks,
};
let session = Box::into_raw(Box::new(session));
let tx = Box::into_raw(Box::new(Mutex::new(tx)));
global_session = Some((session, tx));
*c_session = session;
SP_ERROR_OK
}
#[no_mangle]
pub unsafe extern "C" fn sp_session_release(c_session: *mut sp_session) -> sp_error {
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 {
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();
{
let session = session.session.clone();
SpSession::receive(Future::spawn(move || {
session.login_password(username, password)
}), |session, result| {
result.unwrap();
{
let session = 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 {
let session = &mut *c_session;
let username = session.session.username();
session.cache.intern(&username).as_ptr()
}
#[no_mangle]
pub unsafe extern "C" fn sp_session_user_country(c_session: *mut sp_session) -> c_int {
let session = &*c_session;
let country = session.session.country();
country.chars().fold(0, |acc, x| {
acc << 8 | (x as u32)
}) as c_int
}
#[no_mangle]
pub unsafe extern "C" fn sp_session_process_events(c_session: *mut sp_session, next_timeout: *mut c_int) -> sp_error {
let session = &mut *c_session;
if !next_timeout.is_null() {
*next_timeout = 10;
}
let event = session.rx.recv().unwrap();
event.call_box((session,));
SP_ERROR_OK
}

View file

@ -1,50 +0,0 @@
use libc::{c_int, c_char};
use std::ptr::null_mut;
use artist::sp_artist;
use metadata::SpMetadata;
use session::SpSession;
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 = &mut *c_track;
let name = track.get()
.map(|metadata| &metadata.name as &str)
.unwrap_or("");
track.intern(name).as_ptr()
}
#[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 = SpSession::global();
track.get()
.and_then(|metadata| metadata.artists.get(index as usize).map(|x| *x))
.map(|artist_id| session.session.metadata::<Artist>(artist_id))
.map(|artist| Box::into_raw(Box::new(SpMetadata::from_future(artist))))
.unwrap_or(null_mut())
}

View file

@ -1,148 +0,0 @@
#![allow(non_camel_case_types, dead_code)]
use libc::{size_t, c_int, c_char, c_void};
use session::sp_session;
#[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: c_int,
pub cache_location: *const c_char,
pub settings_location: *const c_char,
pub application_key: *const c_void,
pub application_key_size: size_t,
pub user_agent: *const c_char,
pub callbacks: *const sp_session_callbacks,
pub userdata: *mut c_void,
pub compress_playlists: bool,
pub dont_save_metadata_for_playlists: bool,
pub initially_unload_playlists: bool,
pub device_id: *const c_char,
pub proxy: *const c_char,
pub proxy_username: *const c_char,
pub proxy_password: *const c_char,
pub tracefile: *const c_char,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct sp_session_callbacks {
pub logged_in: Option<unsafe extern "C" fn(session: *mut sp_session,
error: sp_error)>,
pub logged_out: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub metadata_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub connection_error: Option<unsafe extern "C" fn(session: *mut sp_session,
error: sp_error)>,
pub message_to_user: Option<unsafe extern "C" fn(session: *mut sp_session,
message: *const c_char)>,
pub notify_main_thread: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub music_delivery: Option<unsafe extern "C" fn(session: *mut sp_session,
format: *const sp_audioformat,
frames: *const c_void,
num_frames: c_int)
-> c_int>,
pub play_token_lost: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub log_message: Option<unsafe extern "C" fn(session: *mut sp_session,
data: *const c_char)>,
pub end_of_track: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub streaming_error: Option<unsafe extern "C" fn(session: *mut sp_session,
error: sp_error)>,
pub userinfo_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub start_playback: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub stop_playback: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub get_audio_buffer_stats: Option<unsafe extern "C" fn(session: *mut sp_session,
stats: *mut sp_audio_buffer_stats)>,
pub offline_status_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub offline_error: Option<unsafe extern "C" fn(session: *mut sp_session,
error: sp_error)>,
pub credentials_blob_updated: Option<unsafe extern "C" fn(session: *mut sp_session,
blob: *const c_char)>,
pub connectionstate_updated: Option<unsafe extern "C" fn(session: *mut sp_session)>,
pub scrobble_error: Option<unsafe extern "C" fn(session: *mut sp_session,
error: sp_error)>,
pub private_session_mode_changed: Option<unsafe extern "C" fn(session: *mut sp_session,
is_private: bool)>,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct sp_audioformat {
pub sample_type: sp_sampletype,
pub sample_rate: c_int,
pub channels: c_int,
}
#[derive(Clone, Copy)]
#[repr(u32)]
pub enum sp_sampletype {
SP_SAMPLETYPE_INT16_NATIVE_ENDIAN = 0,
_Dummy // rust #10292
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct sp_audio_buffer_stats {
pub samples: c_int,
pub stutter: c_int,
}