mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
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:
parent
4d277e5b75
commit
f207d53943
9 changed files with 0 additions and 564 deletions
|
@ -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 = "../"
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
Loading…
Reference in a new issue