11#[ macro_use]
22extern crate cpython;
3- extern crate tokio_core ;
3+ extern crate futures ;
44extern crate librespot;
5+ extern crate tokio_core;
56
6- use std:: cell:: RefCell ;
7- use cpython:: { PyResult , PyObject } ;
7+ use cpython:: { PyResult , PyObject , Python , PythonObject } ;
8+ use futures:: Future ;
9+ use std:: thread;
810use tokio_core:: reactor:: Core ;
11+ use std:: cell:: RefCell ;
912
10- thread_local ! {
11- pub static CORE : RefCell <Core > = RefCell :: new( Core :: new( ) . unwrap( ) ) ;
13+ // Workaround rust-lang/rust#28796
14+ trait Callback : Send {
15+ fn call ( self : Box < Self > , py : Python ) -> PyResult < PyObject > ;
16+ }
17+ impl < F : Send + for < ' a > FnOnce ( Python < ' a > ) -> PyResult < PyObject > > Callback for F {
18+ fn call ( self : Box < Self > , py : Python ) -> PyResult < PyObject > {
19+ ( * self ) ( py)
20+ }
21+ }
22+
23+ py_class ! ( class PyFuture |py| {
24+ data callback : RefCell <Option <Box <Callback >>>;
25+
26+ def wait( & self ) -> PyResult <PyObject > {
27+ let callback = self . callback( py) . borrow_mut( ) . take( ) . expect( "Future already completed" ) ;
28+ callback. call( py)
29+ }
30+ } ) ;
31+
32+ impl PyFuture {
33+ pub fn new < F , T , U > ( py : Python , future : F , then : T ) -> PyResult < PyFuture >
34+ where F : Future + Send + ' static ,
35+ T : FnOnce ( Python , Result < F :: Item , F :: Error > ) -> PyResult < U > + Send + ' static ,
36+ U : PythonObject
37+ {
38+ PyFuture :: create_instance ( py, RefCell :: new ( Some ( Box :: new ( move |py : Python | {
39+ let result = future. wait ( ) ;
40+ then ( py, result) . map ( PythonObject :: into_object)
41+ } ) ) ) )
42+ }
1243}
1344
1445py_class ! ( class Session |py| {
1546 data session : librespot:: session:: Session ;
1647
17- def __new__ ( _cls, username: String , password: String ) -> PyResult <Session > {
48+ @classmethod def connect ( _cls, username: String , password: String ) -> PyResult <PyFuture > {
1849 use librespot:: session:: Config ;
1950 use librespot:: authentication:: Credentials ;
2051
21- CORE . with( |core| {
22- let mut core = core. borrow_mut( ) ;
52+ let config = Config :: default ( ) ;
53+ let credentials = Credentials :: with_password( username, password) ;
54+
55+ let ( tx, rx) = futures:: sync:: oneshot:: channel( ) ;
56+ thread:: spawn( move || {
57+ let mut core = Core :: new( ) . unwrap( ) ;
2358 let handle = core. handle( ) ;
2459
25- let config = Config :: default ( ) ;
26- let credentials = Credentials :: with_password( username, password) ;
2760 let session = core. run( librespot:: session:: Session :: connect( config, credentials, None , handle) ) . unwrap( ) ;
2861
62+ let _ = tx. send( session) ;
63+
64+ core. run( futures:: future:: empty:: <( ) , ( ) >( ) ) . unwrap( ) ;
65+ } ) ;
66+
67+ PyFuture :: new( py, rx, |py, result| {
68+ let session = result. unwrap( ) ;
2969 Session :: create_instance( py, session)
3070 } )
3171 }
@@ -42,14 +82,12 @@ py_class!(class Session |py| {
4282py_class ! ( class Player |py| {
4383 data player : librespot:: player:: Player ;
4484
45- def play( & self , track: SpotifyId ) -> PyResult <PyObject > {
46- CORE . with( |core| {
47- let mut core = core. borrow_mut( ) ;
48- let player = self . player( py) ;
49- let track = * track. id( py) ;
50-
51- core. run( player. load( track, true , 0 ) ) . unwrap( ) ;
85+ def load( & self , track: SpotifyId , play: bool = true , position_ms: u32 = 0 ) -> PyResult <PyFuture > {
86+ let player = self . player( py) ;
87+ let track = * track. id( py) ;
5288
89+ let end_of_track = player. load( track, play, position_ms) ;
90+ PyFuture :: new( py, end_of_track, |py, _result| {
5391 Ok ( py. None ( ) )
5492 } )
5593 }
0 commit comments