1 /*****************************************************************************
2 * audioscrobbler.c : audioscrobbler submission plugin
3 *****************************************************************************
4 * Copyright (C) 2006 the VideoLAN team
7 * Authors: Rafaël Carré <rafael -dot- carre -at- wanadoo -dot- fr>
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 input_thread_t *p_input; /* previous p_input */
98 audioscrobbler_song_t *p_current_song; /* song being played */
99 time_t time_pause; /* time when vlc paused */
100 time_t time_total_pauses; /* sum of time in pause */
101 vlc_bool_t b_queued; /* has it been queud ? */
102 vlc_bool_t b_metadata_read; /* did we read metadata ? */
103 vlc_bool_t b_paused; /* are we playing ? */
106 intf_sys_t *p_sys_global; /* for use same p_sys in all threads */
108 static int Open ( vlc_object_t * );
109 static void Close ( vlc_object_t * );
110 static void Run ( intf_thread_t * );
111 static int ItemChange ( vlc_object_t *, const char *,
112 vlc_value_t, vlc_value_t, void * );
113 static int PlayingChange( vlc_object_t *, const char *,
114 vlc_value_t, vlc_value_t, void * );
115 static int AddToQueue ( intf_thread_t *p_this );
116 static int Handshake ( intf_thread_t *p_sd );
117 static int ReadMetaData ( intf_thread_t *p_this );
118 void DeleteQueue( audioscrobbler_queue_t *p_queue );
119 char *hexa( short int i );
121 #if !defined(strlwr) && !defined( WIN32 )
122 char* strlwr(char *psz_string);
125 /*****************************************************************************
127 ****************************************************************************/
130 #define APPLICATION_NAME "VLC media player"
131 #define USERNAME_TEXT N_("Username")
132 #define USERNAME_LONGTEXT N_("Audioscrobbler username")
133 #define PASSWORD_TEXT N_("Password")
134 #define PASSWORD_LONGTEXT N_("Audioscrobbler password")
135 #define DEFAULT_INTERVAL 60
136 #define CLIENT_NAME PACKAGE
137 #define CLIENT_VERSION VERSION
139 /* HTTP POST request : to submit data */
140 #define POST_REQUEST "POST /%s HTTP/1.1\n" \
141 "Accept-Encoding: identity\n" \
142 "Content-length: %d\n" \
143 "Connection: close\n" \
144 "Content-type: application/x-www-form-urlencoded\n" \
146 "User-agent: VLC Media Player/%s\r\n" \
152 #define POST_DATA "u=%s&s=%s&a%%5B%d%%5D=%s&t%%5B%d%%5D=%s" \
153 "&b%%5B%d%%5D=%s&m%%5B%d%%5D=&l%%5B%d%%5D=%d&i%%5B%d%%5D=%s"
156 set_category( CAT_INTERFACE );
157 set_subcategory( SUBCAT_INTERFACE_CONTROL );
158 set_shortname( N_( "Audioscrobbler" ) );
159 set_description( _("Audioscrobbler submission Plugin") );
160 add_string( "lastfm-username", "", NULL,
161 USERNAME_TEXT, USERNAME_LONGTEXT, VLC_TRUE );
162 add_string( "lastfm-password", "", NULL,
163 PASSWORD_TEXT, PASSWORD_LONGTEXT, VLC_TRUE );
164 set_capability( "interface", 0 );
165 set_callbacks( Open, Close );
168 /*****************************************************************************
169 * Open: initialize and create stuff
170 *****************************************************************************/
172 static int Open( vlc_object_t *p_this )
174 intf_thread_t *p_intf;
176 playlist_t *p_playlist;
178 p_intf = ( intf_thread_t* ) p_this;
179 p_sys = malloc( sizeof( intf_sys_t ) );
185 vlc_mutex_init( p_this, &p_sys->lock );
187 p_sys_global = p_sys;
188 p_sys->psz_submit_host = NULL;
189 p_sys->psz_submit_file = NULL;
190 p_sys->b_handshaked = VLC_FALSE;
191 p_sys->i_interval = 0;
192 p_sys->time_last_interval = time( NULL );
193 p_sys->psz_username = NULL;
194 p_sys->p_input = NULL;
195 p_sys->b_paused = VLC_FALSE;
197 /* md5 response is 32 chars, + final \0 */
198 p_sys->psz_response_md5 = malloc( sizeof( char ) * 33 );
199 if( !p_sys->psz_response_md5 )
201 vlc_mutex_destroy ( &p_sys->lock );
205 p_sys->p_first_queue = malloc( sizeof( audioscrobbler_queue_t ) );
206 if( !p_sys->p_first_queue )
208 vlc_mutex_destroy( &p_sys->lock );
212 p_sys->p_current_song = malloc( sizeof( audioscrobbler_song_t ) );
213 if( !p_sys->p_current_song )
215 vlc_mutex_destroy( &p_sys->lock );
219 /* queues can't contain more than 10 songs */
220 p_sys->p_first_queue->p_queue =
221 malloc( 10 * sizeof( audioscrobbler_song_t ) );
222 if( !p_sys->p_current_song )
224 vlc_mutex_destroy( &p_sys->lock );
228 p_sys->p_first_queue->i_songs_nb = 0;
229 p_sys->p_first_queue->p_next_queue = NULL;
231 p_playlist = pl_Yield( p_intf );
232 var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
233 pl_Release( p_playlist );
235 p_intf->pf_run = Run;
240 free( p_sys->p_current_song );
241 free( p_sys->p_first_queue );
242 free( p_sys->psz_response_md5 );
248 /*****************************************************************************
249 * Close: destroy interface stuff
250 *****************************************************************************/
251 static void Close( vlc_object_t *p_this )
253 intf_thread_t *p_intf;
255 audioscrobbler_queue_t *p_current_queue, *p_next_queue;
256 playlist_t *p_playlist;
258 p_intf = ( intf_thread_t* ) p_this;
259 p_sys = p_intf->p_sys;
261 p_playlist = pl_Yield( p_intf );
262 var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
263 pl_Release( p_playlist );
265 vlc_mutex_lock ( &p_sys->lock );
266 p_current_queue = p_sys->p_first_queue;
267 vlc_mutex_unlock ( &p_sys->lock );
269 while( ( p_current_queue->i_songs_nb == 10 ) &&
270 ( p_current_queue->p_next_queue != NULL ) )
272 p_next_queue = p_current_queue->p_next_queue;
273 DeleteQueue( p_current_queue );
274 free( p_current_queue );
275 p_current_queue = p_next_queue;
278 DeleteQueue( p_current_queue );
279 free( p_current_queue );
281 vlc_mutex_lock ( &p_sys->lock );
282 if ( p_sys->psz_username )
284 free( p_sys->psz_username );
287 free( p_sys->p_current_song );
288 vlc_mutex_unlock ( &p_sys->lock );
289 vlc_mutex_destroy( &p_sys->lock );
293 /*****************************************************************************
294 * Run : Handshake with audioscrobbler, then submit songs
295 *****************************************************************************/
296 static void Run( intf_thread_t *p_this )
298 char *psz_submit_string = NULL;
302 playlist_t *p_playlist;
303 uint8_t *p_buffer = NULL;
304 char *p_buffer_pos = NULL;
306 audioscrobbler_queue_t *p_first_queue;
309 p_this->p_sys = p_sys_global;
310 p_sys = p_this->p_sys;
311 while( !p_this->b_die )
313 if( p_sys->b_handshaked == VLC_FALSE )
316 ( p_sys->time_last_interval + p_sys->i_interval ) )
318 msg_Dbg( p_this, "Handshaking with last.fm ..." );
319 i_handshake = Handshake( p_this );
321 if( i_handshake == VLC_ENOMEM )
323 msg_Err( p_this, "Out of memory" );
327 else if( i_handshake == VLC_ENOVAR )
328 /* username not set */
330 msg_Dbg( p_this, "Set an username then restart vlc" );
331 vlc_mutex_unlock ( &p_sys->lock );
335 else if( i_handshake == VLC_SUCCESS )
337 msg_Dbg( p_this, "Handshake successfull :)" );
338 vlc_mutex_lock ( &p_sys->lock );
339 p_sys->b_handshaked = VLC_TRUE;
340 vlc_mutex_unlock ( &p_sys->lock );
345 vlc_mutex_lock ( &p_sys->lock );
346 p_sys->i_interval = DEFAULT_INTERVAL;
347 time( &p_sys->time_last_interval );
348 vlc_mutex_unlock ( &p_sys->lock );
355 if ( ( p_sys->p_first_queue->i_songs_nb > 0 ) &&
357 ( p_sys->time_last_interval + p_sys->i_interval ) ) )
359 msg_Dbg( p_this, "Going to submit some data..." );
360 vlc_mutex_lock ( &p_sys->lock );
361 psz_submit_string = malloc( 2048 * sizeof( char ) );
363 if (!psz_submit_string)
365 msg_Err( p_this, "Out of memory" );
366 vlc_mutex_unlock ( &p_sys->lock );
370 for (i_song = 0; i_song < p_sys->p_first_queue->i_songs_nb ;
373 snprintf( psz_submit_string, 2048, POST_DATA,
374 p_sys->psz_username, p_sys->psz_response_md5,
375 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_a,
376 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_t,
377 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_b,
379 i_song, p_sys->p_first_queue->p_queue[i_song]->i_l,
380 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_i
384 p_sys->i_post_socket = net_ConnectTCP( p_this,
385 p_sys->psz_submit_host, p_sys->i_submit_port);
387 i_netprintf = net_Printf(
388 VLC_OBJECT(p_this), p_sys->i_post_socket, NULL,
389 POST_REQUEST, p_sys->psz_submit_file,
390 strlen( psz_submit_string), p_sys->psz_submit_file,
391 VERSION, psz_submit_string
394 if ( i_netprintf == -1 )
396 /* If connection fails, we assume we must handshake again */
397 p_sys->i_interval = DEFAULT_INTERVAL;
398 time( &p_sys->time_last_interval );
399 p_sys->b_handshaked = VLC_FALSE;
400 vlc_mutex_unlock ( &p_sys->lock );
404 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
407 msg_Err( p_this, "Out of memory" );
408 vlc_mutex_unlock ( &p_sys->lock );
412 net_Read( p_this, p_sys->i_post_socket, NULL,
413 p_buffer, 1024, VLC_FALSE );
414 net_Close( p_sys->i_post_socket );
416 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
420 p_sys->i_interval = atoi( p_buffer_pos +
421 strlen( "INTERVAL " ) );
422 time( &p_sys->time_last_interval );
425 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
429 msg_Err( p_this, p_buffer_pos );
430 vlc_mutex_unlock ( &p_sys->lock );
434 p_buffer_pos = strstr( ( char * ) p_buffer, "BADAUTH" );
439 "Authentification failed, handshaking again" );
440 p_sys->b_handshaked = VLC_FALSE;
441 vlc_mutex_unlock ( &p_sys->lock );
445 p_buffer_pos = strstr( ( char * ) p_buffer, "OK" );
449 if ( p_sys->p_first_queue->i_songs_nb == 10 )
451 p_first_queue = p_sys->p_first_queue->p_next_queue;
452 DeleteQueue( p_sys->p_first_queue );
453 free( p_sys->p_first_queue );
454 p_sys->p_first_queue = p_first_queue;
458 DeleteQueue( p_sys->p_first_queue );
459 p_sys->p_first_queue->i_songs_nb = 0;
461 msg_Dbg( p_this, "Submission successfull!" );
463 vlc_mutex_unlock ( &p_sys->lock );
466 msleep( INTF_IDLE_SLEEP );
468 p_playlist = pl_Yield( p_this );
470 if( p_playlist->request.i_status == PLAYLIST_STOPPED )
473 /* if we stopped, we won't submit playing song */
474 vlc_mutex_lock( &p_sys->lock );
475 p_sys->b_queued = VLC_TRUE;
476 p_sys->b_metadata_read = VLC_TRUE;
477 vlc_mutex_unlock( &p_sys->lock );
484 pl_Release( p_playlist );
485 vlc_mutex_lock( &p_sys->lock );
487 if( p_sys->b_metadata_read == VLC_FALSE )
489 time( &played_time );
490 played_time -= p_sys->p_current_song->time_playing;
491 played_time -= p_sys->time_total_pauses;
494 vlc_mutex_unlock( &p_sys->lock );
496 /* ok now we can read meta data */
497 if( played_time > 10 )
499 ReadMetaData( p_this );
504 if( ( p_sys->b_queued == VLC_FALSE )
505 && ( p_sys->b_paused == VLC_FALSE ) )
507 vlc_mutex_unlock( &p_sys->lock );
508 if( AddToQueue( p_this ) == VLC_ENOMEM )
510 msg_Err( p_this, "Out of memory" );
516 vlc_mutex_unlock( &p_sys->lock );
522 /*****************************************************************************
523 * PlayingChange: Playing status change callback
524 *****************************************************************************/
525 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
526 vlc_value_t oldval, vlc_value_t newval, void *p_data )
528 intf_thread_t *p_intf;
531 p_intf = ( intf_thread_t* ) p_data;
532 p_sys = p_intf->p_sys;
534 (void)p_this; (void)psz_var; (void)oldval;
536 vlc_mutex_lock( &p_sys->lock );
538 if( newval.i_int == PAUSE_S )
540 time( &p_sys->time_pause );
541 p_sys->b_paused = VLC_TRUE;
544 if( newval.i_int == PLAYING_S )
546 p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
547 p_sys->b_paused = VLC_TRUE;
550 vlc_mutex_unlock( &p_sys->lock );
555 /*****************************************************************************
556 * ItemChange: Playlist item change callback
557 *****************************************************************************/
558 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
559 vlc_value_t oldval, vlc_value_t newval, void *p_data )
561 playlist_t *p_playlist;
562 input_thread_t *p_input = NULL;
563 intf_thread_t *p_intf;
571 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
573 p_intf = ( intf_thread_t* ) p_data;
574 p_sys = p_intf->p_sys;
576 vlc_mutex_lock ( &p_sys->lock );
578 if ( p_sys->p_input )
580 /* we delete the callback for the old p_input */
581 var_DelCallback( p_sys->p_input, "state", PlayingChange, p_intf );
584 vlc_mutex_unlock ( &p_sys->lock );
586 p_playlist = pl_Yield( p_intf );
588 p_input = p_playlist->p_input;
593 pl_Release( p_playlist );
594 vlc_mutex_lock( &p_sys->lock );
596 /* we won't read p_input */
597 p_sys->b_queued = VLC_TRUE;
598 p_sys->b_metadata_read = VLC_TRUE;
600 vlc_mutex_unlock( &p_sys->lock );
604 vlc_object_yield( p_input );
606 pl_Release( p_playlist );
608 var_AddCallback( p_input, "state", PlayingChange, p_intf );
610 vlc_mutex_lock ( &p_sys->lock );
612 /* reset pause counter */
613 p_sys->time_total_pauses = 0;
615 /* we save the p_input value to delete the callback later */
616 p_sys->p_input = p_input;
618 /* we'll read after to be sure it's present */
619 p_sys->b_metadata_read = VLC_FALSE;
621 p_sys->b_queued = VLC_TRUE;
624 epoch_tm = gmtime( &epoch );
625 snprintf( psz_date, 20, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
626 epoch_tm->tm_year+1900, epoch_tm->tm_mon+1, epoch_tm->tm_mday,
627 epoch_tm->tm_hour, epoch_tm->tm_min, epoch_tm->tm_sec );
629 p_sys->p_current_song->psz_i = encode_URI_component( psz_date );
630 p_sys->p_current_song->time_playing = epoch;
632 p_sys->b_paused = ( p_input->b_dead || !p_input->input.p_item->psz_name )
633 ? VLC_TRUE : VLC_FALSE;
635 vlc_mutex_unlock( &p_sys->lock );
637 vlc_object_release( p_input );
641 /*****************************************************************************
642 * AddToQueue: Add the played song to the queue to be submitted
643 *****************************************************************************/
644 static int AddToQueue ( intf_thread_t *p_this )
648 audioscrobbler_queue_t *p_queue = NULL, *p_next_queue = NULL;
651 p_sys = p_this->p_sys;
653 /* has it been queued already ? is it long enough ? */
654 vlc_mutex_lock( &p_sys->lock );
655 if( p_sys->p_current_song->i_l < 30 )
657 msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
658 p_sys->b_queued = VLC_TRUE;
659 vlc_mutex_unlock ( &p_sys->lock );
663 /* wait for the user to listen enough before submitting */
664 time ( &played_time );
665 played_time -= p_sys->p_current_song->time_playing;
666 played_time -= p_sys->time_total_pauses;
668 if( ( played_time < 240 )
669 && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
671 vlc_mutex_unlock ( &p_sys->lock );
675 msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
677 p_queue = p_sys->p_first_queue;
679 while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
681 p_queue = p_queue->p_next_queue;
684 i_songs_nb = p_queue->i_songs_nb;
686 if( i_songs_nb == 10 )
688 p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
691 vlc_mutex_unlock ( &p_sys->lock );
694 p_queue->p_next_queue = p_next_queue;
696 p_queue = p_next_queue;
697 p_queue->i_songs_nb = i_songs_nb;
700 p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
702 p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
704 p_queue->p_queue[i_songs_nb]->psz_a =
705 strdup( p_sys->p_current_song->psz_a );
707 p_queue->p_queue[i_songs_nb]->psz_t =
708 strdup( p_sys->p_current_song->psz_t );
710 p_queue->p_queue[i_songs_nb]->psz_b =
711 strdup( p_sys->p_current_song->psz_b );
713 p_queue->p_queue[i_songs_nb]->psz_i =
714 strdup( p_sys->p_current_song->psz_i );
716 p_queue->i_songs_nb++;
717 p_sys->b_queued = VLC_TRUE;
719 vlc_mutex_unlock( &p_sys->lock );
724 /*****************************************************************************
725 * Handshake : Init audioscrobbler connection
726 *****************************************************************************/
727 static int Handshake( intf_thread_t *p_this )
729 char *psz_password = NULL;
730 struct md5_s *p_struct_md5 = NULL;
731 char *psz_password_md5 = NULL;
732 char *ps_challenge_md5 = NULL;
735 char *psz_handshake_url = NULL;
737 uint8_t *p_buffer = NULL;
738 char *p_buffer_pos = NULL;
739 char *psz_buffer_substring = NULL;
740 char *psz_url_parser = NULL;
743 char *b1, *b2, *b3, *b4;
745 intf_thread_t *p_intf;
748 p_intf = ( intf_thread_t* ) p_this;
749 p_sys = p_this->p_sys;
751 vlc_mutex_lock ( &p_sys->lock );
753 p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
754 if ( !p_sys->psz_username )
760 if ( !*p_sys->psz_username )
762 msg_Info( p_this, "You have to set an username! "
763 "Visit https://www.last.fm/join/" );
767 psz_handshake_url = malloc( 1024 );
768 if ( !psz_handshake_url )
773 snprintf( psz_handshake_url, 1024,
774 "http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s",
775 CLIENT_NAME, CLIENT_VERSION, p_sys->psz_username );
777 p_stream = stream_UrlNew( p_intf, psz_handshake_url);
779 free( psz_handshake_url );
783 p_sys->i_interval = DEFAULT_INTERVAL;
784 time( &p_sys->time_last_interval );
785 vlc_mutex_unlock ( &p_sys->lock );
789 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
792 stream_Delete( p_stream );
796 if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
798 stream_Delete( p_stream );
800 vlc_mutex_unlock ( &p_sys->lock );
804 stream_Delete( p_stream );
806 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
810 p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
811 time( &p_sys->time_last_interval );
814 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
818 msg_Info( p_this, p_buffer_pos );
820 vlc_mutex_unlock ( &p_sys->lock );
824 p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
828 msg_Info( p_intf, "Username is incorrect" );
830 vlc_mutex_unlock ( &p_sys->lock );
834 p_buffer_pos = strstr( ( char * ) p_buffer, "UPDATE" );
838 msg_Dbg( p_intf, "Protocol updated" );
839 msg_Dbg( p_intf, p_buffer_pos );
844 p_buffer_pos = strstr( ( char * ) p_buffer, "UPTODATE" );
847 msg_Dbg( p_intf, "Protocol error" );
849 vlc_mutex_unlock ( &p_sys->lock );
854 psz_buffer_substring = strndup( strstr( p_buffer_pos, "\n" ) + 1, 32 );
855 if ( !psz_buffer_substring )
861 ps_challenge_md5 = malloc( sizeof( char ) * 32 );
862 if ( !ps_challenge_md5 )
866 memcpy( ps_challenge_md5, psz_buffer_substring, 32 );
867 free( psz_buffer_substring );
870 p_buffer_pos = ( void* ) strstr( ( char* ) p_buffer, "http://" );
872 if ( p_sys->psz_submit_host != NULL )
874 free( p_sys->psz_submit_host );
877 if ( p_sys->psz_submit_file != NULL )
879 free( p_sys->psz_submit_file );
882 psz_url_parser = p_buffer_pos + strlen( "http://" );
884 i_url_pos = strcspn( psz_url_parser, ":" );
885 p_sys->psz_submit_host = strndup( psz_url_parser, i_url_pos );
887 p_sys->i_submit_port = atoi( psz_url_parser + i_url_pos + 1 );
889 psz_url_parser += strcspn( psz_url_parser , "/" ) + 1;
890 i_url_pos = strcspn( psz_url_parser, "\n" );
891 p_sys->psz_submit_file = strndup( psz_url_parser, i_url_pos );
895 p_struct_md5 = malloc( sizeof( struct md5_s ) );
901 psz_password = config_GetPsz(p_this, "lastfm-password");
907 InitMD5( p_struct_md5 );
908 AddMD5( p_struct_md5, ( uint8_t* ) psz_password, strlen( psz_password ) );
909 EndMD5( p_struct_md5 );
911 free( psz_password );
913 psz_password_md5 = malloc ( 33 * sizeof( char ) );
914 if ( !psz_password_md5 )
919 for ( i = 0; i < 4; i++ )
921 /* TODO check that this works on every arch/platform (uint32_t to char) */
922 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
923 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
924 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
925 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
926 sprintf( &psz_password_md5[8*i], "%s%s%s%s", b1, b2, b3, b4 );
933 strlwr( psz_password_md5 );
935 InitMD5( p_struct_md5 );
936 AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
937 AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
938 EndMD5( p_struct_md5 );
940 free( ps_challenge_md5 );
941 free( psz_password_md5 );
943 for ( i = 0; i < 4; i++ )
945 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
946 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
947 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
948 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
949 sprintf( &p_sys->psz_response_md5[8*i],"%s%s%s%s", b1, b2, b3, b4 );
956 p_sys->psz_response_md5[32] = 0;
958 strlwr( p_sys->psz_response_md5 );
960 vlc_mutex_unlock ( &p_sys->lock );
966 free( p_struct_md5 );
967 free( psz_buffer_substring );
969 vlc_mutex_unlock ( &p_sys->lock );
973 /*****************************************************************************
974 * hexa : Converts a byte to a string in its hexadecimal value
975 *****************************************************************************/
976 char *hexa( short int i )
978 char *res = calloc( 3 , sizeof( char ) );
980 ((i/16) < 10) ? res[0] = (i / 16) + '0' : ( res[0] = (i/16) + 'a' - 10 );
981 ((i%16) < 10) ? res[1] = (i % 16) + '0' : ( res[1] = (i%16) + 'a' - 10 );
985 /*****************************************************************************
986 * strlwr : Converts a string to lower case
987 *****************************************************************************/
988 #if !defined(strlwr) && !defined( WIN32 )
989 char* strlwr(char *psz_string)
991 while ( *psz_string )
993 *psz_string++ = tolower( *psz_string );
999 /*****************************************************************************
1000 * DeleteQueue : Free all songs from an audioscrobbler_queue_t
1001 *****************************************************************************/
1002 void DeleteQueue( audioscrobbler_queue_t *p_queue )
1006 for( i = 0; i < p_queue->i_songs_nb; i++ )
1008 free( p_queue->p_queue[i]->psz_a );
1009 free( p_queue->p_queue[i]->psz_b );
1010 free( p_queue->p_queue[i]->psz_t );
1011 free( p_queue->p_queue[i]->psz_i );
1012 free( p_queue->p_queue[i] );
1016 /*****************************************************************************
1017 * ReadMetaData : Puts current song's meta data in p_sys->p_current_song
1018 *****************************************************************************/
1019 static int ReadMetaData( intf_thread_t *p_this )
1021 playlist_t *p_playlist;
1022 char *psz_title = NULL;
1023 char *psz_artist = NULL;
1024 char *psz_album = NULL;
1026 input_thread_t *p_input = NULL;
1027 vlc_value_t video_val;
1030 p_sys = p_this->p_sys;
1031 p_playlist = pl_Yield( p_this );
1033 p_input = p_playlist->p_input;
1040 vlc_object_yield( p_input );
1042 pl_Release( p_playlist );
1044 if ( p_input->input.p_item->i_type == ITEM_TYPE_NET )
1046 msg_Dbg( p_this, "We play a stream -> no submission");
1050 var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
1051 if( video_val.i_int > 0 )
1053 msg_Dbg( p_this, "We play a video -> no submission");
1057 if ( p_input->input.p_item->p_meta->psz_artist )
1059 psz_artist = encode_URI_component(
1060 p_input->input.p_item->p_meta->psz_artist );
1068 msg_Dbg( p_this, "No artist.." );
1072 if ( p_input->input.p_item->p_meta->psz_album )
1074 psz_album = encode_URI_component(
1075 p_input->input.p_item->p_meta->psz_album );
1083 msg_Dbg( p_this, "No album.." );
1087 if ( p_input->input.p_item->psz_name )
1089 psz_title = encode_URI_component( p_input->input.p_item->psz_name );
1097 msg_Dbg( p_this, "No track name.." );
1101 i_length = p_input->input.p_item->i_duration / 1000000;
1103 vlc_object_release( p_input );
1105 vlc_mutex_lock ( &p_sys->lock );
1107 p_sys->p_current_song->psz_a = strdup( psz_artist );
1108 p_sys->p_current_song->psz_t = strdup( psz_title );
1109 p_sys->p_current_song->psz_b = strdup( psz_album );
1110 p_sys->p_current_song->i_l = i_length;
1111 p_sys->b_queued = VLC_FALSE;
1112 p_sys->b_metadata_read = VLC_TRUE;
1114 vlc_mutex_unlock ( &p_sys->lock );
1116 msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1131 vlc_object_release( p_input );
1137 vlc_mutex_lock( &p_sys->lock );
1138 p_sys->b_queued = VLC_TRUE;
1139 p_sys->b_metadata_read = VLC_TRUE;
1140 vlc_mutex_unlock( &p_sys->lock );