瀏覽代碼

Initial C API

Paul Lietar 9 年之前
父節點
當前提交
ae38e60518
共有 8 個文件被更改,包括 394 次插入0 次删除
  1. 16 0
      capi/Cargo.toml
  2. 33 0
      capi/src/artist.rs
  3. 17 0
      capi/src/lib.rs
  4. 36 0
      capi/src/link.rs
  5. 54 0
      capi/src/metadata.rs
  6. 110 0
      capi/src/session.rs
  7. 60 0
      capi/src/track.rs
  8. 68 0
      capi/src/types.rs

+ 16 - 0
capi/Cargo.toml

@@ -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 - 0
capi/src/artist.rs

@@ -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 - 0
capi/src/lib.rs

@@ -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 - 0
capi/src/link.rs

@@ -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 - 0
capi/src/metadata.rs

@@ -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 - 0
capi/src/session.rs

@@ -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 - 0
capi/src/track.rs

@@ -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 - 0
capi/src/types.rs

@@ -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,
+}
+