1 /*****************************************************************************
2 * audioscrobbler.c : audioscrobbler submission plugin
3 *****************************************************************************
4 * Copyright (C) 2006 the VideoLAN team
7 * Authors: Rafaël Carré <rafael -dot- carre -at- gmail -dot- com>
8 * Kenneth Ostby <kenneo -at- idi -dot- ntnu -dot- no>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
32 #if !defined( strlwr ) && !defined( WIN32 )
49 #include <vlc_block.h>
50 #include <vlc_stream.h>
54 /*****************************************************************************
56 *****************************************************************************/
58 /* Keeps track of metadata to be submitted, and if song has been submitted */
59 typedef struct audioscrobbler_song_t
61 char *psz_a; /* track artist */
62 char *psz_t; /* track title */
63 char *psz_b; /* track album */
64 int i_l; /* track length */
65 /* vlc can't retrieve musicbrainz id, so let's ignore it */
66 /* int i_m; */ /* musicbrainz id */
67 char *psz_i; /* date */
68 time_t time_playing; /* date (epoch) */
69 } audioscrobbler_song_t;
72 /* Queue to be submitted to server, 10 songs max */
73 typedef struct audioscrobbler_queue_t
75 audioscrobbler_song_t **p_queue; /* contains up to 10 songs */
76 int i_songs_nb; /* number of songs */
77 void *p_next_queue; /* if queue full, pointer to next */
78 } audioscrobbler_queue_t;
82 audioscrobbler_queue_t *p_first_queue; /* 1st queue */
83 vlc_mutex_t lock; /* p_sys mutex */
85 /* data about audioscrobbler session */
86 int i_interval; /* last interval recorded */
87 time_t time_last_interval; /* when was it recorded ? */
88 char *psz_submit_host; /* where to submit data ? */
89 int i_submit_port; /* at which port ? */
90 char *psz_submit_file; /* in which file ? */
91 char *psz_username; /* last.fm username */
92 vlc_bool_t b_handshaked; /* did we handshake ? */
93 int i_post_socket; /* socket for submission */
94 char *psz_response_md5; /* md5 response to use */
96 /* data about input elements */
97 audioscrobbler_song_t *p_current_song; /* song being played */
98 time_t time_pause; /* time when vlc paused */
99 time_t time_total_pauses; /* sum of time in pause */
100 vlc_bool_t b_queued; /* has it been queud ? */
101 vlc_bool_t b_metadata_read; /* did we read metadata ? */
102 vlc_bool_t b_paused; /* are we playing ? */
105 intf_sys_t *p_sys_global; /* for use same p_sys in all threads */
107 static int Open ( vlc_object_t * );
108 static void Close ( vlc_object_t * );
109 static void Run ( intf_thread_t * );
110 static int ItemChange ( vlc_object_t *, const char *,
111 vlc_value_t, vlc_value_t, void * );
112 static int PlayingChange( vlc_object_t *, const char *,
113 vlc_value_t, vlc_value_t, void * );
114 static int AddToQueue ( intf_thread_t *p_this );
115 static int Handshake ( intf_thread_t *p_sd );
116 static int ReadMetaData ( intf_thread_t *p_this );
117 void DeleteQueue( audioscrobbler_queue_t *p_queue );
118 char *hexa( short int i );
120 #if !defined(strlwr) && !defined( WIN32 )
121 char* strlwr(char *psz_string);
124 /*****************************************************************************
126 ****************************************************************************/
129 #define APPLICATION_NAME "VLC media player"
130 #define USERNAME_TEXT N_("Username")
131 #define USERNAME_LONGTEXT N_("Audioscrobbler username")
132 #define PASSWORD_TEXT N_("Password")
133 #define PASSWORD_LONGTEXT N_("Audioscrobbler password")
134 #define DEFAULT_INTERVAL 60
135 #define CLIENT_NAME PACKAGE
136 #define CLIENT_VERSION VERSION
138 /* HTTP POST request : to submit data */
139 #define POST_REQUEST "POST /%s HTTP/1.1\n" \
140 "Accept-Encoding: identity\n" \
141 "Content-length: %d\n" \
142 "Connection: close\n" \
143 "Content-type: application/x-www-form-urlencoded\n" \
145 "User-agent: VLC Media Player/%s\r\n" \
151 #define POST_DATA "u=%s&s=%s&a%%5B%d%%5D=%s&t%%5B%d%%5D=%s" \
152 "&b%%5B%d%%5D=%s&m%%5B%d%%5D=&l%%5B%d%%5D=%d&i%%5B%d%%5D=%s"
155 set_category( CAT_INTERFACE );
156 set_subcategory( SUBCAT_INTERFACE_CONTROL );
157 set_shortname( N_( "Audioscrobbler" ) );
158 set_description( _("Audioscrobbler submission Plugin") );
159 add_string( "lastfm-username", "", NULL,
160 USERNAME_TEXT, USERNAME_LONGTEXT, VLC_TRUE );
161 add_string( "lastfm-password", "", NULL,
162 PASSWORD_TEXT, PASSWORD_LONGTEXT, VLC_TRUE );
163 set_capability( "interface", 0 );
164 set_callbacks( Open, Close );
167 /*****************************************************************************
168 * Open: initialize and create stuff
169 *****************************************************************************/
171 static int Open( vlc_object_t *p_this )
173 playlist_t *p_playlist;
175 intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
176 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
182 vlc_mutex_init( p_this, &p_sys->lock );
184 p_sys_global = p_sys;
185 p_sys->psz_submit_host = NULL;
186 p_sys->psz_submit_file = NULL;
187 p_sys->b_handshaked = VLC_FALSE;
188 p_sys->i_interval = 0;
189 p_sys->time_last_interval = time( NULL );
190 p_sys->psz_username = NULL;
191 p_sys->b_paused = VLC_FALSE;
193 /* md5 response is 32 chars, + final \0 */
194 p_sys->psz_response_md5 = malloc( sizeof( char ) * 33 );
195 if( !p_sys->psz_response_md5 )
197 vlc_mutex_destroy ( &p_sys->lock );
201 p_sys->p_first_queue = malloc( sizeof( audioscrobbler_queue_t ) );
202 if( !p_sys->p_first_queue )
204 vlc_mutex_destroy( &p_sys->lock );
208 p_sys->p_current_song = malloc( sizeof( audioscrobbler_song_t ) );
209 if( !p_sys->p_current_song )
211 vlc_mutex_destroy( &p_sys->lock );
215 /* queues can't contain more than 10 songs */
216 p_sys->p_first_queue->p_queue =
217 malloc( 10 * sizeof( audioscrobbler_song_t ) );
218 if( !p_sys->p_current_song )
220 vlc_mutex_destroy( &p_sys->lock );
224 p_sys->p_first_queue->i_songs_nb = 0;
225 p_sys->p_first_queue->p_next_queue = NULL;
227 p_playlist = pl_Yield( p_intf );
228 var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
229 pl_Release( p_playlist );
231 p_intf->pf_run = Run;
236 free( p_sys->p_current_song );
237 free( p_sys->p_first_queue );
238 free( p_sys->psz_response_md5 );
244 /*****************************************************************************
245 * Close: destroy interface stuff
246 *****************************************************************************/
247 static void Close( vlc_object_t *p_this )
249 audioscrobbler_queue_t *p_current_queue, *p_next_queue;
250 playlist_t *p_playlist;
251 input_thread_t *p_input;
253 intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
254 intf_sys_t *p_sys = p_intf->p_sys;
256 p_playlist = pl_Yield( p_intf );
258 p_input = p_playlist->p_input;
259 var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
263 vlc_object_yield( p_input );
264 var_DelCallback( p_input, "state", PlayingChange, p_intf );
265 vlc_object_release( p_input );
269 pl_Release( p_playlist );
271 vlc_mutex_lock ( &p_sys->lock );
272 p_current_queue = p_sys->p_first_queue;
273 vlc_mutex_unlock ( &p_sys->lock );
275 while( ( p_current_queue->i_songs_nb == 10 ) &&
276 ( p_current_queue->p_next_queue != NULL ) )
278 p_next_queue = p_current_queue->p_next_queue;
279 DeleteQueue( p_current_queue );
280 free( p_current_queue );
281 p_current_queue = p_next_queue;
284 DeleteQueue( p_current_queue );
285 free( p_current_queue );
287 vlc_mutex_lock ( &p_sys->lock );
288 if ( p_sys->psz_username )
290 free( p_sys->psz_username );
293 free( p_sys->p_current_song );
294 vlc_mutex_unlock ( &p_sys->lock );
295 vlc_mutex_destroy( &p_sys->lock );
299 /*****************************************************************************
300 * Run : Handshake with audioscrobbler, then submit songs
301 *****************************************************************************/
302 static void Run( intf_thread_t *p_this )
304 char *psz_submit_string = NULL;
308 playlist_t *p_playlist;
309 uint8_t *p_buffer = NULL;
310 char *p_buffer_pos = NULL;
312 audioscrobbler_queue_t *p_first_queue;
314 p_this->p_sys = p_sys_global;
315 intf_sys_t *p_sys = p_this->p_sys;
317 while( !p_this->b_die )
319 if( p_sys->b_handshaked == VLC_FALSE )
322 ( p_sys->time_last_interval + p_sys->i_interval ) )
324 msg_Dbg( p_this, "Handshaking with last.fm ..." );
325 i_handshake = Handshake( p_this );
327 if( i_handshake == VLC_ENOMEM )
329 msg_Err( p_this, "Out of memory" );
333 else if( i_handshake == VLC_ENOVAR )
334 /* username not set */
336 msg_Dbg( p_this, "Set an username then restart vlc" );
337 vlc_mutex_unlock ( &p_sys->lock );
341 else if( i_handshake == VLC_SUCCESS )
343 msg_Dbg( p_this, "Handshake successfull :)" );
344 vlc_mutex_lock ( &p_sys->lock );
345 p_sys->b_handshaked = VLC_TRUE;
346 vlc_mutex_unlock ( &p_sys->lock );
351 vlc_mutex_lock ( &p_sys->lock );
352 p_sys->i_interval = DEFAULT_INTERVAL;
353 time( &p_sys->time_last_interval );
354 vlc_mutex_unlock ( &p_sys->lock );
361 if ( ( p_sys->p_first_queue->i_songs_nb > 0 ) &&
363 ( p_sys->time_last_interval + p_sys->i_interval ) ) )
365 msg_Dbg( p_this, "Going to submit some data..." );
366 vlc_mutex_lock ( &p_sys->lock );
367 psz_submit_string = malloc( 2048 * sizeof( char ) );
369 if (!psz_submit_string)
371 msg_Err( p_this, "Out of memory" );
372 vlc_mutex_unlock ( &p_sys->lock );
376 for (i_song = 0; i_song < p_sys->p_first_queue->i_songs_nb ;
379 snprintf( psz_submit_string, 2048, POST_DATA,
380 p_sys->psz_username, p_sys->psz_response_md5,
381 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_a,
382 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_t,
383 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_b,
385 i_song, p_sys->p_first_queue->p_queue[i_song]->i_l,
386 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_i
390 p_sys->i_post_socket = net_ConnectTCP( p_this,
391 p_sys->psz_submit_host, p_sys->i_submit_port);
393 i_netprintf = net_Printf(
394 VLC_OBJECT(p_this), p_sys->i_post_socket, NULL,
395 POST_REQUEST, p_sys->psz_submit_file,
396 strlen( psz_submit_string), p_sys->psz_submit_file,
397 VERSION, psz_submit_string
400 if ( i_netprintf == -1 )
402 /* If connection fails, we assume we must handshake again */
403 p_sys->i_interval = DEFAULT_INTERVAL;
404 time( &p_sys->time_last_interval );
405 p_sys->b_handshaked = VLC_FALSE;
406 vlc_mutex_unlock ( &p_sys->lock );
410 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
413 msg_Err( p_this, "Out of memory" );
414 vlc_mutex_unlock ( &p_sys->lock );
418 net_Read( p_this, p_sys->i_post_socket, NULL,
419 p_buffer, 1024, VLC_FALSE );
420 net_Close( p_sys->i_post_socket );
422 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
426 p_sys->i_interval = atoi( p_buffer_pos +
427 strlen( "INTERVAL " ) );
428 time( &p_sys->time_last_interval );
431 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
435 msg_Err( p_this, p_buffer_pos );
436 vlc_mutex_unlock ( &p_sys->lock );
440 p_buffer_pos = strstr( ( char * ) p_buffer, "BADAUTH" );
445 "Authentification failed, handshaking again" );
446 p_sys->b_handshaked = VLC_FALSE;
447 vlc_mutex_unlock ( &p_sys->lock );
451 p_buffer_pos = strstr( ( char * ) p_buffer, "OK" );
455 if ( p_sys->p_first_queue->i_songs_nb == 10 )
457 p_first_queue = p_sys->p_first_queue->p_next_queue;
458 DeleteQueue( p_sys->p_first_queue );
459 free( p_sys->p_first_queue );
460 p_sys->p_first_queue = p_first_queue;
464 DeleteQueue( p_sys->p_first_queue );
465 p_sys->p_first_queue->i_songs_nb = 0;
467 msg_Dbg( p_this, "Submission successfull!" );
469 vlc_mutex_unlock ( &p_sys->lock );
472 msleep( INTF_IDLE_SLEEP );
474 p_playlist = pl_Yield( p_this );
476 if( p_playlist->request.i_status == PLAYLIST_STOPPED )
479 pl_Release( p_playlist );
480 /* if we stopped, we won't submit playing song */
481 vlc_mutex_lock( &p_sys->lock );
482 p_sys->b_queued = VLC_TRUE;
483 p_sys->b_metadata_read = VLC_TRUE;
484 vlc_mutex_unlock( &p_sys->lock );
489 pl_Release( p_playlist );
492 vlc_mutex_lock( &p_sys->lock );
494 if( p_sys->b_metadata_read == VLC_FALSE )
496 time( &played_time );
497 played_time -= p_sys->p_current_song->time_playing;
498 played_time -= p_sys->time_total_pauses;
501 vlc_mutex_unlock( &p_sys->lock );
503 /* ok now we can read meta data */
504 if( played_time > 10 )
506 ReadMetaData( p_this );
511 if( ( p_sys->b_queued == VLC_FALSE )
512 && ( p_sys->b_paused == VLC_FALSE ) )
514 vlc_mutex_unlock( &p_sys->lock );
515 if( AddToQueue( p_this ) == VLC_ENOMEM )
517 msg_Err( p_this, "Out of memory" );
523 vlc_mutex_unlock( &p_sys->lock );
529 /*****************************************************************************
530 * PlayingChange: Playing status change callback
531 *****************************************************************************/
532 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
533 vlc_value_t oldval, vlc_value_t newval, void *p_data )
535 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
536 intf_sys_t *p_sys = p_intf->p_sys;
538 (void)p_this; (void)psz_var; (void)oldval;
540 vlc_mutex_lock( &p_sys->lock );
542 if( newval.i_int == PAUSE_S )
544 time( &p_sys->time_pause );
545 p_sys->b_paused = VLC_TRUE;
548 if( newval.i_int == PLAYING_S )
550 p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
551 p_sys->b_paused = VLC_TRUE;
554 vlc_mutex_unlock( &p_sys->lock );
559 /*****************************************************************************
560 * ItemChange: Playlist item change callback
561 *****************************************************************************/
562 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
563 vlc_value_t oldval, vlc_value_t newval, void *p_data )
565 playlist_t *p_playlist;
566 input_thread_t *p_input = NULL;
571 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
573 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
574 intf_sys_t *p_sys = p_intf->p_sys;
576 p_playlist = pl_Yield( p_intf );
578 p_input = p_playlist->p_input;
583 pl_Release( p_playlist );
584 vlc_mutex_lock( &p_sys->lock );
586 /* we won't read p_input */
587 p_sys->b_queued = VLC_TRUE;
588 p_sys->b_metadata_read = VLC_TRUE;
590 vlc_mutex_unlock( &p_sys->lock );
594 vlc_object_yield( p_input );
596 pl_Release( p_playlist );
598 var_AddCallback( p_input, "state", PlayingChange, p_intf );
600 vlc_mutex_lock ( &p_sys->lock );
602 /* reset pause counter */
603 p_sys->time_total_pauses = 0;
605 /* we'll read after to be sure it's present */
606 p_sys->b_metadata_read = VLC_FALSE;
608 p_sys->b_queued = VLC_TRUE;
611 epoch_tm = gmtime( &epoch );
612 snprintf( psz_date, 20, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
613 epoch_tm->tm_year+1900, epoch_tm->tm_mon+1, epoch_tm->tm_mday,
614 epoch_tm->tm_hour, epoch_tm->tm_min, epoch_tm->tm_sec );
616 p_sys->p_current_song->psz_i = encode_URI_component( psz_date );
617 p_sys->p_current_song->time_playing = epoch;
619 p_sys->b_paused = ( p_input->b_dead || !p_input->input.p_item->psz_name )
620 ? VLC_TRUE : VLC_FALSE;
622 vlc_mutex_unlock( &p_sys->lock );
624 vlc_object_release( p_input );
628 /*****************************************************************************
629 * AddToQueue: Add the played song to the queue to be submitted
630 *****************************************************************************/
631 static int AddToQueue ( intf_thread_t *p_this )
635 audioscrobbler_queue_t *p_queue = NULL, *p_next_queue = NULL;
637 intf_sys_t *p_sys = p_this->p_sys;
639 /* has it been queued already ? is it long enough ? */
640 vlc_mutex_lock( &p_sys->lock );
641 if( p_sys->p_current_song->i_l < 30 )
643 msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
644 p_sys->b_queued = VLC_TRUE;
645 vlc_mutex_unlock ( &p_sys->lock );
649 /* wait for the user to listen enough before submitting */
650 time ( &played_time );
651 played_time -= p_sys->p_current_song->time_playing;
652 played_time -= p_sys->time_total_pauses;
654 if( ( played_time < 240 )
655 && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
657 vlc_mutex_unlock ( &p_sys->lock );
661 msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
663 p_queue = p_sys->p_first_queue;
665 while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
667 p_queue = p_queue->p_next_queue;
670 i_songs_nb = p_queue->i_songs_nb;
672 if( i_songs_nb == 10 )
674 p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
677 vlc_mutex_unlock ( &p_sys->lock );
680 p_queue->p_next_queue = p_next_queue;
682 p_queue = p_next_queue;
683 p_queue->i_songs_nb = i_songs_nb;
686 p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
688 p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
690 p_queue->p_queue[i_songs_nb]->psz_a =
691 strdup( p_sys->p_current_song->psz_a );
693 p_queue->p_queue[i_songs_nb]->psz_t =
694 strdup( p_sys->p_current_song->psz_t );
696 p_queue->p_queue[i_songs_nb]->psz_b =
697 strdup( p_sys->p_current_song->psz_b );
699 p_queue->p_queue[i_songs_nb]->psz_i =
700 strdup( p_sys->p_current_song->psz_i );
702 p_queue->i_songs_nb++;
703 p_sys->b_queued = VLC_TRUE;
705 vlc_mutex_unlock( &p_sys->lock );
710 /*****************************************************************************
711 * Handshake : Init audioscrobbler connection
712 *****************************************************************************/
713 static int Handshake( intf_thread_t *p_this )
715 char *psz_password = NULL;
716 struct md5_s *p_struct_md5 = NULL;
717 char *psz_password_md5 = NULL;
718 char *ps_challenge_md5 = NULL;
721 char *psz_handshake_url = NULL;
723 uint8_t *p_buffer = NULL;
724 char *p_buffer_pos = NULL;
725 char *psz_buffer_substring = NULL;
726 char *psz_url_parser = NULL;
729 char *b1, *b2, *b3, *b4;
731 intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
732 intf_sys_t *p_sys = p_this->p_sys;
734 vlc_mutex_lock ( &p_sys->lock );
736 p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
737 if ( !p_sys->psz_username )
743 if ( !*p_sys->psz_username )
745 msg_Info( p_this, "You have to set an username! "
746 "Visit https://www.last.fm/join/" );
750 psz_handshake_url = malloc( 1024 );
751 if ( !psz_handshake_url )
756 snprintf( psz_handshake_url, 1024,
757 "http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s",
758 CLIENT_NAME, CLIENT_VERSION, p_sys->psz_username );
760 p_stream = stream_UrlNew( p_intf, psz_handshake_url);
762 free( psz_handshake_url );
766 p_sys->i_interval = DEFAULT_INTERVAL;
767 time( &p_sys->time_last_interval );
768 vlc_mutex_unlock ( &p_sys->lock );
772 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
775 stream_Delete( p_stream );
779 if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
781 stream_Delete( p_stream );
783 vlc_mutex_unlock ( &p_sys->lock );
787 stream_Delete( p_stream );
789 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
793 p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
794 time( &p_sys->time_last_interval );
797 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
801 msg_Info( p_this, p_buffer_pos );
803 vlc_mutex_unlock ( &p_sys->lock );
807 p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
811 msg_Info( p_intf, "Username is incorrect" );
813 vlc_mutex_unlock ( &p_sys->lock );
817 p_buffer_pos = strstr( ( char * ) p_buffer, "UPDATE" );
821 msg_Dbg( p_intf, "Protocol updated" );
822 msg_Dbg( p_intf, p_buffer_pos );
827 p_buffer_pos = strstr( ( char * ) p_buffer, "UPTODATE" );
830 msg_Dbg( p_intf, "Protocol error" );
832 vlc_mutex_unlock ( &p_sys->lock );
837 psz_buffer_substring = strndup( strstr( p_buffer_pos, "\n" ) + 1, 32 );
838 if ( !psz_buffer_substring )
844 ps_challenge_md5 = malloc( sizeof( char ) * 32 );
845 if ( !ps_challenge_md5 )
849 memcpy( ps_challenge_md5, psz_buffer_substring, 32 );
850 free( psz_buffer_substring );
853 p_buffer_pos = ( void* ) strstr( ( char* ) p_buffer, "http://" );
855 if ( p_sys->psz_submit_host != NULL )
857 free( p_sys->psz_submit_host );
860 if ( p_sys->psz_submit_file != NULL )
862 free( p_sys->psz_submit_file );
865 psz_url_parser = p_buffer_pos + strlen( "http://" );
867 i_url_pos = strcspn( psz_url_parser, ":" );
868 p_sys->psz_submit_host = strndup( psz_url_parser, i_url_pos );
870 p_sys->i_submit_port = atoi( psz_url_parser + i_url_pos + 1 );
872 psz_url_parser += strcspn( psz_url_parser , "/" ) + 1;
873 i_url_pos = strcspn( psz_url_parser, "\n" );
874 p_sys->psz_submit_file = strndup( psz_url_parser, i_url_pos );
878 p_struct_md5 = malloc( sizeof( struct md5_s ) );
884 psz_password = config_GetPsz(p_this, "lastfm-password");
890 InitMD5( p_struct_md5 );
891 AddMD5( p_struct_md5, ( uint8_t* ) psz_password, strlen( psz_password ) );
892 EndMD5( p_struct_md5 );
894 free( psz_password );
896 psz_password_md5 = malloc ( 33 * sizeof( char ) );
897 if ( !psz_password_md5 )
902 for ( i = 0; i < 4; i++ )
904 /* TODO check that this works on every arch/platform (uint32_t to char) */
905 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
906 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
907 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
908 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
909 sprintf( &psz_password_md5[8*i], "%s%s%s%s", b1, b2, b3, b4 );
916 strlwr( psz_password_md5 );
918 InitMD5( p_struct_md5 );
919 AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
920 AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
921 EndMD5( p_struct_md5 );
923 free( ps_challenge_md5 );
924 free( psz_password_md5 );
926 for ( i = 0; i < 4; i++ )
928 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
929 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
930 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
931 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
932 sprintf( &p_sys->psz_response_md5[8*i],"%s%s%s%s", b1, b2, b3, b4 );
939 p_sys->psz_response_md5[32] = 0;
941 strlwr( p_sys->psz_response_md5 );
943 vlc_mutex_unlock ( &p_sys->lock );
949 free( p_struct_md5 );
950 free( psz_buffer_substring );
952 vlc_mutex_unlock ( &p_sys->lock );
956 /*****************************************************************************
957 * hexa : Converts a byte to a string in its hexadecimal value
958 *****************************************************************************/
959 char *hexa( short int i )
961 char *res = calloc( 3 , sizeof( char ) );
963 ((i/16) < 10) ? res[0] = (i / 16) + '0' : ( res[0] = (i/16) + 'a' - 10 );
964 ((i%16) < 10) ? res[1] = (i % 16) + '0' : ( res[1] = (i%16) + 'a' - 10 );
968 /*****************************************************************************
969 * strlwr : Converts a string to lower case
970 *****************************************************************************/
971 #if !defined(strlwr) && !defined( WIN32 )
972 char* strlwr(char *psz_string)
974 while ( *psz_string )
976 *psz_string++ = tolower( *psz_string );
982 /*****************************************************************************
983 * DeleteQueue : Free all songs from an audioscrobbler_queue_t
984 *****************************************************************************/
985 void DeleteQueue( audioscrobbler_queue_t *p_queue )
989 for( i = 0; i < p_queue->i_songs_nb; i++ )
991 free( p_queue->p_queue[i]->psz_a );
992 free( p_queue->p_queue[i]->psz_b );
993 free( p_queue->p_queue[i]->psz_t );
994 free( p_queue->p_queue[i]->psz_i );
995 free( p_queue->p_queue[i] );
999 /*****************************************************************************
1000 * ReadMetaData : Puts current song's meta data in p_sys->p_current_song
1001 *****************************************************************************/
1002 static int ReadMetaData( intf_thread_t *p_this )
1004 playlist_t *p_playlist;
1005 char *psz_title = NULL;
1006 char *psz_artist = NULL;
1007 char *psz_album = NULL;
1009 input_thread_t *p_input = NULL;
1010 vlc_value_t video_val;
1012 intf_sys_t *p_sys = p_this->p_sys;
1014 p_playlist = pl_Yield( p_this );
1016 p_input = p_playlist->p_input;
1021 pl_Release( p_playlist );
1022 return( VLC_SUCCESS );
1025 vlc_object_yield( p_input );
1027 pl_Release( p_playlist );
1029 if ( p_input->input.p_item->i_type == ITEM_TYPE_NET )
1031 msg_Dbg( p_this, "We play a stream -> no submission");
1035 var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
1036 if( video_val.i_int > 0 )
1038 msg_Dbg( p_this, "We play a video -> no submission");
1042 if ( p_input->input.p_item->p_meta->psz_artist )
1044 psz_artist = encode_URI_component(
1045 p_input->input.p_item->p_meta->psz_artist );
1053 msg_Dbg( p_this, "No artist.." );
1057 if ( p_input->input.p_item->p_meta->psz_album )
1059 psz_album = encode_URI_component(
1060 p_input->input.p_item->p_meta->psz_album );
1068 psz_album = calloc( 1, sizeof( char ) );
1071 if ( p_input->input.p_item->psz_name )
1073 psz_title = encode_URI_component( p_input->input.p_item->psz_name );
1081 msg_Dbg( p_this, "No track name.." );
1085 i_length = p_input->input.p_item->i_duration / 1000000;
1087 vlc_object_release( p_input );
1089 vlc_mutex_lock ( &p_sys->lock );
1091 p_sys->p_current_song->psz_a = strdup( psz_artist );
1092 p_sys->p_current_song->psz_t = strdup( psz_title );
1093 p_sys->p_current_song->psz_b = strdup( psz_album );
1094 p_sys->p_current_song->i_l = i_length;
1095 p_sys->b_queued = VLC_FALSE;
1096 p_sys->b_metadata_read = VLC_TRUE;
1098 vlc_mutex_unlock ( &p_sys->lock );
1100 msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1109 vlc_object_release( p_input );
1117 vlc_object_release( p_input );
1123 vlc_mutex_lock( &p_sys->lock );
1124 p_sys->b_queued = VLC_TRUE;
1125 p_sys->b_metadata_read = VLC_TRUE;
1126 vlc_mutex_unlock( &p_sys->lock );