@@ -2,107 +2,205 @@ use jni::objects::JClass;
22use jni:: sys:: { jint, jlong} ;
33use jni:: JNIEnv ;
44use std:: ffi:: CString ;
5+ use std:: os:: raw:: c_char;
56
67use futures:: executor:: block_on;
8+ use windows:: Win32 :: Foundation :: E_FAIL ;
79use windows:: { core:: * , Media :: Control :: * } ;
810
9- use std:: os:: raw:: c_char;
10-
1111mod session;
1212pub use session:: get_spotify_session;
1313
14+ #[ no_mangle]
15+ pub extern "system" fn isSpotifyAvailable ( _env : JNIEnv , _class : JClass ) -> jint {
16+ let result = std:: panic:: catch_unwind ( || {
17+ block_on ( async {
18+ match get_spotify_session ( ) {
19+ Ok ( Some ( _session) ) => Ok ( 1 ) ,
20+ Ok ( None ) => Ok ( 0 ) ,
21+ Err ( _) => Err ( ( ) ) ,
22+ }
23+ } )
24+ } ) ;
25+
26+ match result {
27+ Ok ( Ok ( val) ) => val,
28+ _ => 0 ,
29+ }
30+ }
31+
1432#[ no_mangle]
1533pub extern "system" fn getPlaybackPosition ( _env : JNIEnv , _class : JClass ) -> jlong {
16- let result: Result < jlong > = block_on ( async {
17- match get_spotify_session ( ) ? {
18- Some ( session) => {
19- let timeline = session. GetTimelineProperties ( ) ?;
20- let position = timeline. Position ( ) ?. Duration ;
21-
22- // Convert ticks to ms
23- Ok ( ( position / 10_000 ) as jlong )
34+ let result = std:: panic:: catch_unwind ( || {
35+ block_on ( async {
36+ match get_spotify_session ( ) {
37+ Ok ( Some ( session) ) => {
38+ let timeline = session. GetTimelineProperties ( ) ?;
39+ let position = timeline. Position ( ) ?;
40+ Ok ( ( position. Duration / 10_000 ) as jlong )
41+ }
42+ _ => Err ( Error :: new ( E_FAIL , "Spotify session not available" ) ) ,
2443 }
25- None => Ok ( 0 ) ,
26- }
44+ } )
2745 } ) ;
28- result. unwrap_or ( 0 )
46+
47+ match result {
48+ Ok ( Ok ( position) ) => position,
49+ _ => -1 ,
50+ }
2951}
3052
3153#[ no_mangle]
3254pub extern "system" fn getTrackDuration ( _env : JNIEnv , _class : JClass ) -> jlong {
33- let result: Result < jlong > = block_on ( async {
34- match get_spotify_session ( ) ? {
35- Some ( session) => {
36- let timeline = session. GetTimelineProperties ( ) ?;
37- let end = timeline. EndTime ( ) ?. Duration ;
38-
39- // Convert ticks to ms
40- Ok ( ( end / 10_000 ) as jlong )
55+ let result = std:: panic:: catch_unwind ( || {
56+ block_on ( async {
57+ match get_spotify_session ( ) ? {
58+ Some ( session) => {
59+ let timeline = session. GetTimelineProperties ( ) ?;
60+ let duration = timeline. EndTime ( ) ?. Duration ;
61+ Ok ( ( duration / 10_000 ) as jlong )
62+ }
63+ _ => Err ( Error :: new ( E_FAIL , "Spotify session not found" ) ) ,
4164 }
42- None => Ok ( 0 ) ,
43- }
65+ } )
4466 } ) ;
45- result. unwrap_or ( 0 )
67+
68+ match result {
69+ Ok ( Ok ( duration) ) => duration,
70+ _ => -1 ,
71+ }
4672}
4773
4874#[ no_mangle]
4975pub extern "system" fn getTrackTitle ( _env : JNIEnv , _class : JClass ) -> * const c_char {
50- let track_title = block_on ( async {
51- match get_spotify_session ( ) {
52- Ok ( Some ( session) ) => {
53- let props = session. TryGetMediaPropertiesAsync ( ) . unwrap ( ) . get ( ) . unwrap ( ) ;
54- let title = props. Title ( ) . unwrap ( ) ;
55- title. to_string ( )
76+ let result = std:: panic:: catch_unwind ( || {
77+ block_on ( async {
78+ match get_spotify_session ( ) ? {
79+ Some ( session) => {
80+ let props = session. TryGetMediaPropertiesAsync ( ) ?. get ( ) ?;
81+ let title = props. Title ( ) ?. to_string ( ) ;
82+ // Safely create CString, fallback to empty string if null bytes present
83+ Ok ( CString :: new ( title) . unwrap_or_default ( ) . into_raw ( ) )
84+ }
85+ _ => Err ( Error :: new ( E_FAIL , "Spotify session not found" ) ) ,
5686 }
57- _ => "" . to_string ( ) ,
58- }
87+ } )
5988 } ) ;
6089
61- // Convert Rust String to CString
62- let cstring = CString :: new ( track_title) . unwrap_or_else ( |_| CString :: new ( "" ) . unwrap ( ) ) ;
63- cstring. into_raw ( )
90+ match result {
91+ Ok ( Ok ( ptr) ) => ptr,
92+ _ => std:: ptr:: null ( ) ,
93+ }
6494}
6595
6696#[ no_mangle]
6797pub extern "system" fn getArtistName ( _env : JNIEnv , _class : JClass ) -> * const c_char {
68- let artist_name = block_on ( async {
69- match get_spotify_session ( ) {
70- Ok ( Some ( session) ) => {
71- let props = session. TryGetMediaPropertiesAsync ( ) . unwrap ( ) . get ( ) . unwrap ( ) ;
72- let artist = props. Artist ( ) . unwrap ( ) ;
73- artist. to_string ( )
98+ let result = std:: panic:: catch_unwind ( || {
99+ block_on ( async {
100+ match get_spotify_session ( ) ? {
101+ Some ( session) => {
102+ let props = session. TryGetMediaPropertiesAsync ( ) ?. get ( ) ?;
103+ let artist = props. Artist ( ) ?. to_string ( ) ;
104+ Ok ( CString :: new ( artist) . unwrap_or_default ( ) . into_raw ( ) )
105+ }
106+ _ => Err ( Error :: new ( E_FAIL , "Spotify session not found" ) ) ,
74107 }
75- _ => "" . to_string ( ) ,
76- }
108+ } )
77109 } ) ;
78110
79- // Convert Rust String to CString
80- let cstring = CString :: new ( artist_name) . unwrap_or_else ( |_| CString :: new ( "" ) . unwrap ( ) ) ;
81- cstring. into_raw ( )
111+ match result {
112+ Ok ( Ok ( ptr) ) => ptr,
113+ _ => std:: ptr:: null ( ) ,
114+ }
82115}
83116
84117#[ no_mangle]
85118pub extern "system" fn isPlaying ( _env : JNIEnv , _class : JClass ) -> jint {
86- let result: Result < jint > = block_on ( async {
87- match get_spotify_session ( ) ? {
88- Some ( session) => {
89- let playback_info = session. GetPlaybackInfo ( ) ?;
90- let status = playback_info. PlaybackStatus ( ) ?;
91- let is_playing = ( status
92- == GlobalSystemMediaTransportControlsSessionPlaybackStatus :: Playing )
93- as jint ;
94- Ok ( is_playing)
119+ let result = std:: panic:: catch_unwind ( || {
120+ block_on ( async {
121+ match get_spotify_session ( ) ? {
122+ Some ( session) => {
123+ let playback_info = session. GetPlaybackInfo ( ) ?;
124+ let status = playback_info. PlaybackStatus ( ) ?;
125+ Ok (
126+ ( status == GlobalSystemMediaTransportControlsSessionPlaybackStatus :: Playing )
127+ as jint ,
128+ )
129+ }
130+ _ => Err ( Error :: new ( E_FAIL , "Spotify session not found" ) ) ,
95131 }
96- None => Ok ( 0 ) ,
97- }
132+ } )
98133 } ) ;
99- result. unwrap_or ( 0 )
134+
135+ match result {
136+ Ok ( Ok ( val) ) => val,
137+ _ => -1 ,
138+ }
139+ }
140+
141+ #[ no_mangle]
142+ pub extern "system" fn getCoverArt ( out_ptr : * mut * mut u8 , out_len : * mut usize ) -> i32 {
143+ if out_ptr. is_null ( ) || out_len. is_null ( ) {
144+ return 0 ; // false
145+ }
146+ let result = std:: panic:: catch_unwind ( || {
147+ block_on ( async {
148+ match get_spotify_session ( ) ? {
149+ Some ( session) => {
150+ let props = session. TryGetMediaPropertiesAsync ( ) ?. get ( ) ?;
151+ let thumbnail = props. Thumbnail ( ) ?;
152+ let stream = thumbnail. OpenReadAsync ( ) ?. get ( ) ?;
153+ let size = stream. Size ( ) ?;
154+
155+ if size == 0 {
156+ return Err ( Error :: new ( E_FAIL , "Cover art size is zero" ) ) ;
157+ }
158+
159+ use windows:: Storage :: Streams :: DataReader ;
160+ let reader = DataReader :: CreateDataReader ( & stream) ?;
161+ reader. LoadAsync ( size as u32 ) ?. get ( ) ?;
162+
163+ let mut buffer = vec ! [ 0u8 ; size as usize ] ;
164+ reader. ReadBytes ( & mut buffer) ?;
165+ Ok ( buffer)
166+ }
167+ _ => Err ( Error :: empty ( ) ) ,
168+ }
169+ } )
170+ } ) ;
171+
172+ match result {
173+ Ok ( Ok ( buffer) ) => unsafe {
174+ let len = buffer. len ( ) ;
175+ let ptr = libc:: malloc ( len) ;
176+ if ptr. is_null ( ) {
177+ return 0 ; // false
178+ }
179+ std:: ptr:: copy_nonoverlapping ( buffer. as_ptr ( ) , ptr as * mut u8 , len) ;
180+ * out_ptr = ptr as * mut u8 ;
181+ * out_len = len;
182+ 1 // true
183+ } ,
184+ _ => {
185+ 0 // false
186+ }
187+ }
100188}
101189
102190#[ no_mangle]
103191pub extern "system" fn freeString ( s : * mut c_char ) {
104- if s. is_null ( ) { return ; }
192+ if !s. is_null ( ) {
193+ unsafe {
194+ let _ = CString :: from_raw ( s) ;
195+ }
196+ }
197+ }
198+
199+ #[ no_mangle]
200+ pub extern "system" fn freeCoverArt ( ptr : * mut u8 ) {
105201 unsafe {
106- let _ = CString :: from_raw ( s) ;
202+ if !ptr. is_null ( ) {
203+ libc:: free ( ptr as * mut _ ) ;
204+ }
107205 }
108206}
0 commit comments