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 );
480 pl_Release( p_playlist );
481 vlc_mutex_lock( &p_sys->lock );
483 if( p_sys->b_metadata_read == VLC_FALSE )
485 time( &played_time );
486 played_time -= p_sys->p_current_song->time_playing;
487 played_time -= p_sys->time_total_pauses;
490 vlc_mutex_unlock( &p_sys->lock );
492 /* ok now we can read meta data */
493 if( played_time > 10 )
495 ReadMetaData( p_this );
500 if( ( p_sys->b_queued == VLC_FALSE )
501 && ( p_sys->b_paused == VLC_FALSE ) )
503 vlc_mutex_unlock( &p_sys->lock );
504 if( AddToQueue( p_this ) == VLC_ENOMEM )
506 msg_Err( p_this, "Out of memory" );
512 vlc_mutex_unlock( &p_sys->lock );
518 /*****************************************************************************
519 * PlayingChange: Playing status change callback
520 *****************************************************************************/
521 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
522 vlc_value_t oldval, vlc_value_t newval, void *p_data )
524 intf_thread_t *p_intf;
527 p_intf = ( intf_thread_t* ) p_data;
528 p_sys = p_intf->p_sys;
530 (void)p_this; (void)psz_var; (void)oldval;
532 vlc_mutex_lock( &p_sys->lock );
534 if( newval.i_int == PAUSE_S )
536 time( &p_sys->time_pause );
537 p_sys->b_paused = VLC_TRUE;
540 if( newval.i_int == PLAYING_S )
542 p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
543 p_sys->b_paused = VLC_TRUE;
546 vlc_mutex_unlock( &p_sys->lock );
551 /*****************************************************************************
552 * ItemChange: Playlist item change callback
553 *****************************************************************************/
554 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
555 vlc_value_t oldval, vlc_value_t newval, void *p_data )
557 playlist_t *p_playlist;
558 input_thread_t *p_input = NULL;
559 intf_thread_t *p_intf;
567 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
569 p_intf = ( intf_thread_t* ) p_data;
570 p_sys = p_intf->p_sys;
572 vlc_mutex_lock ( &p_sys->lock );
574 if ( p_sys->p_input )
576 /* we delete the callback for the old p_input */
577 var_DelCallback( p_sys->p_input, "state", PlayingChange, p_intf );
580 vlc_mutex_unlock ( &p_sys->lock );
582 p_playlist = pl_Yield( p_intf );
584 p_input = p_playlist->p_input;
589 pl_Release( p_playlist );
590 vlc_mutex_lock( &p_sys->lock );
592 /* we won't read p_input */
593 p_sys->b_queued = VLC_TRUE;
594 p_sys->b_metadata_read = VLC_TRUE;
596 vlc_mutex_unlock( &p_sys->lock );
600 vlc_object_yield( p_input );
602 pl_Release( p_playlist );
604 var_AddCallback( p_input, "state", PlayingChange, p_intf );
606 vlc_mutex_lock ( &p_sys->lock );
608 /* reset pause counter */
609 p_sys->time_total_pauses = 0;
611 /* we save the p_input value to delete the callback later */
612 p_sys->p_input = p_input;
614 /* we'll read after to be sure it's present */
615 p_sys->b_metadata_read = VLC_FALSE;
617 p_sys->b_queued = VLC_TRUE;
620 epoch_tm = gmtime( &epoch );
621 snprintf( psz_date, 20, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
622 epoch_tm->tm_year+1900, epoch_tm->tm_mon+1, epoch_tm->tm_mday,
623 epoch_tm->tm_hour, epoch_tm->tm_min, epoch_tm->tm_sec );
625 p_sys->p_current_song->psz_i = encode_URI_component( psz_date );
626 p_sys->p_current_song->time_playing = epoch;
628 p_sys->b_paused = ( p_input->b_dead || !p_input->input.p_item->psz_name )
629 ? VLC_TRUE : VLC_FALSE;
631 vlc_mutex_unlock( &p_sys->lock );
633 vlc_object_release( p_input );
637 /*****************************************************************************
638 * AddToQueue: Add the played song to the queue to be submitted
639 *****************************************************************************/
640 static int AddToQueue ( intf_thread_t *p_this )
644 audioscrobbler_queue_t *p_queue = NULL, *p_next_queue = NULL;
647 p_sys = p_this->p_sys;
649 /* has it been queued already ? is it long enough ? */
650 vlc_mutex_lock( &p_sys->lock );
651 if( p_sys->p_current_song->i_l < 30 )
653 msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
654 p_sys->b_queued = VLC_TRUE;
655 vlc_mutex_unlock ( &p_sys->lock );
659 /* wait for the user to listen enough before submitting */
660 time ( &played_time );
661 played_time -= p_sys->p_current_song->time_playing;
662 played_time -= p_sys->time_total_pauses;
664 if( ( played_time < 240 )
665 && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
667 vlc_mutex_unlock ( &p_sys->lock );
671 msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
673 p_queue = p_sys->p_first_queue;
675 while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
677 p_queue = p_queue->p_next_queue;
680 i_songs_nb = p_queue->i_songs_nb;
682 if( i_songs_nb == 10 )
684 p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
687 vlc_mutex_unlock ( &p_sys->lock );
690 p_queue->p_next_queue = p_next_queue;
692 p_queue = p_next_queue;
693 p_queue->i_songs_nb = i_songs_nb;
696 p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
698 p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
700 p_queue->p_queue[i_songs_nb]->psz_a =
701 strdup( p_sys->p_current_song->psz_a );
703 p_queue->p_queue[i_songs_nb]->psz_t =
704 strdup( p_sys->p_current_song->psz_t );
706 p_queue->p_queue[i_songs_nb]->psz_b =
707 strdup( p_sys->p_current_song->psz_b );
709 p_queue->p_queue[i_songs_nb]->psz_i =
710 strdup( p_sys->p_current_song->psz_i );
712 p_queue->i_songs_nb++;
713 p_sys->b_queued = VLC_TRUE;
715 vlc_mutex_unlock( &p_sys->lock );
720 /*****************************************************************************
721 * Handshake : Init audioscrobbler connection
722 *****************************************************************************/
723 static int Handshake( intf_thread_t *p_this )
725 char *psz_password = NULL;
726 struct md5_s *p_struct_md5 = NULL;
727 char *psz_password_md5 = NULL;
728 char *ps_challenge_md5 = NULL;
731 char *psz_handshake_url = NULL;
733 uint8_t *p_buffer = NULL;
734 char *p_buffer_pos = NULL;
735 char *psz_buffer_substring = NULL;
736 char *psz_url_parser = NULL;
739 char *b1, *b2, *b3, *b4;
741 intf_thread_t *p_intf;
744 p_intf = ( intf_thread_t* ) p_this;
745 p_sys = p_this->p_sys;
747 vlc_mutex_lock ( &p_sys->lock );
749 p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
750 if ( !p_sys->psz_username )
756 if ( !*p_sys->psz_username )
758 msg_Info( p_this, "You have to set an username! "
759 "Visit https://www.last.fm/join/" );
763 psz_handshake_url = malloc( 1024 );
764 if ( !psz_handshake_url )
769 snprintf( psz_handshake_url, 1024,
770 "http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s",
771 CLIENT_NAME, CLIENT_VERSION, p_sys->psz_username );
773 p_stream = stream_UrlNew( p_intf, psz_handshake_url);
775 free( psz_handshake_url );
779 p_sys->i_interval = DEFAULT_INTERVAL;
780 time( &p_sys->time_last_interval );
781 vlc_mutex_unlock ( &p_sys->lock );
785 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
788 stream_Delete( p_stream );
792 if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
794 stream_Delete( p_stream );
796 vlc_mutex_unlock ( &p_sys->lock );
800 stream_Delete( p_stream );
802 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
806 p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
807 time( &p_sys->time_last_interval );
810 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
814 msg_Info( p_this, p_buffer_pos );
816 vlc_mutex_unlock ( &p_sys->lock );
820 p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
824 msg_Info( p_intf, "Username is incorrect" );
826 vlc_mutex_unlock ( &p_sys->lock );
830 p_buffer_pos = strstr( ( char * ) p_buffer, "UPDATE" );
834 msg_Dbg( p_intf, "Protocol updated" );
835 msg_Dbg( p_intf, p_buffer_pos );
840 p_buffer_pos = strstr( ( char * ) p_buffer, "UPTODATE" );
843 msg_Dbg( p_intf, "Protocol error" );
845 vlc_mutex_unlock ( &p_sys->lock );
850 psz_buffer_substring = strndup( strstr( p_buffer_pos, "\n" ) + 1, 32 );
851 if ( !psz_buffer_substring )
857 ps_challenge_md5 = malloc( sizeof( char ) * 32 );
858 if ( !ps_challenge_md5 )
862 memcpy( ps_challenge_md5, psz_buffer_substring, 32 );
863 free( psz_buffer_substring );
866 p_buffer_pos = ( void* ) strstr( ( char* ) p_buffer, "http://" );
868 if ( p_sys->psz_submit_host != NULL )
870 free( p_sys->psz_submit_host );
873 if ( p_sys->psz_submit_file != NULL )
875 free( p_sys->psz_submit_file );
878 psz_url_parser = p_buffer_pos + strlen( "http://" );
880 i_url_pos = strcspn( psz_url_parser, ":" );
881 p_sys->psz_submit_host = strndup( psz_url_parser, i_url_pos );
883 p_sys->i_submit_port = atoi( psz_url_parser + i_url_pos + 1 );
885 psz_url_parser += strcspn( psz_url_parser , "/" ) + 1;
886 i_url_pos = strcspn( psz_url_parser, "\n" );
887 p_sys->psz_submit_file = strndup( psz_url_parser, i_url_pos );
891 p_struct_md5 = malloc( sizeof( struct md5_s ) );
897 psz_password = config_GetPsz(p_this, "lastfm-password");
903 InitMD5( p_struct_md5 );
904 AddMD5( p_struct_md5, ( uint8_t* ) psz_password, strlen( psz_password ) );
905 EndMD5( p_struct_md5 );
907 free( psz_password );
909 psz_password_md5 = malloc ( 33 * sizeof( char ) );
910 if ( !psz_password_md5 )
915 for ( i = 0; i < 4; i++ )
917 /* TODO check that this works on every arch/platform (uint32_t to char) */
918 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
919 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
920 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
921 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
922 sprintf( &psz_password_md5[8*i], "%s%s%s%s", b1, b2, b3, b4 );
929 strlwr( psz_password_md5 );
931 InitMD5( p_struct_md5 );
932 AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
933 AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
934 EndMD5( p_struct_md5 );
936 free( ps_challenge_md5 );
937 free( psz_password_md5 );
939 for ( i = 0; i < 4; i++ )
941 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
942 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
943 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
944 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
945 sprintf( &p_sys->psz_response_md5[8*i],"%s%s%s%s", b1, b2, b3, b4 );
952 p_sys->psz_response_md5[32] = 0;
954 strlwr( p_sys->psz_response_md5 );
956 vlc_mutex_unlock ( &p_sys->lock );
962 free( p_struct_md5 );
963 free( psz_buffer_substring );
965 vlc_mutex_unlock ( &p_sys->lock );
969 /*****************************************************************************
970 * hexa : Converts a byte to a string in its hexadecimal value
971 *****************************************************************************/
972 char *hexa( short int i )
974 char *res = calloc( 3 , sizeof( char ) );
976 ((i/16) < 10) ? res[0] = (i / 16) + '0' : ( res[0] = (i/16) + 'a' - 10 );
977 ((i%16) < 10) ? res[1] = (i % 16) + '0' : ( res[1] = (i%16) + 'a' - 10 );
981 /*****************************************************************************
982 * strlwr : Converts a string to lower case
983 *****************************************************************************/
984 #if !defined(strlwr) && !defined( WIN32 )
985 char* strlwr(char *psz_string)
987 while ( *psz_string )
989 *psz_string++ = tolower( *psz_string );
995 /*****************************************************************************
996 * DeleteQueue : Free all songs from an audioscrobbler_queue_t
997 *****************************************************************************/
998 void DeleteQueue( audioscrobbler_queue_t *p_queue )
1002 for( i = 0; i < p_queue->i_songs_nb; i++ )
1004 free( p_queue->p_queue[i]->psz_a );
1005 free( p_queue->p_queue[i]->psz_b );
1006 free( p_queue->p_queue[i]->psz_t );
1007 free( p_queue->p_queue[i]->psz_i );
1008 free( p_queue->p_queue[i] );
1012 /*****************************************************************************
1013 * ReadMetaData : Puts current song's meta data in p_sys->p_current_song
1014 *****************************************************************************/
1015 static int ReadMetaData( intf_thread_t *p_this )
1017 playlist_t *p_playlist;
1018 char *psz_title = NULL;
1019 char *psz_artist = NULL;
1020 char *psz_album = NULL;
1022 input_thread_t *p_input = NULL;
1023 vlc_value_t video_val;
1026 p_sys = p_this->p_sys;
1027 p_playlist = pl_Yield( p_this );
1029 p_input = p_playlist->p_input;
1036 vlc_object_yield( p_input );
1038 pl_Release( p_playlist );
1040 if ( p_input->input.p_item->i_type == ITEM_TYPE_NET )
1042 msg_Dbg( p_this, "We play a stream -> no submission");
1046 var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
1047 if( video_val.i_int > 0 )
1049 msg_Dbg( p_this, "We play a video -> no submission");
1053 if ( p_input->input.p_item->p_meta->psz_artist )
1055 psz_artist = encode_URI_component(
1056 p_input->input.p_item->p_meta->psz_artist );
1064 msg_Dbg( p_this, "No artist.." );
1068 if ( p_input->input.p_item->p_meta->psz_album )
1070 psz_album = encode_URI_component(
1071 p_input->input.p_item->p_meta->psz_album );
1079 msg_Dbg( p_this, "No album.." );
1083 if ( p_input->input.p_item->psz_name )
1085 psz_title = encode_URI_component( p_input->input.p_item->psz_name );
1093 msg_Dbg( p_this, "No track name.." );
1097 i_length = p_input->input.p_item->i_duration / 1000000;
1099 vlc_object_release( p_input );
1101 vlc_mutex_lock ( &p_sys->lock );
1103 p_sys->p_current_song->psz_a = strdup( psz_artist );
1104 p_sys->p_current_song->psz_t = strdup( psz_title );
1105 p_sys->p_current_song->psz_b = strdup( psz_album );
1106 p_sys->p_current_song->i_l = i_length;
1107 p_sys->b_queued = VLC_FALSE;
1108 p_sys->b_metadata_read = VLC_TRUE;
1110 vlc_mutex_unlock ( &p_sys->lock );
1112 msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1127 vlc_object_release( p_input );
1133 vlc_mutex_lock( &p_sys->lock );
1134 p_sys->b_queued = VLC_TRUE;
1135 p_sys->b_metadata_read = VLC_TRUE;
1136 vlc_mutex_unlock( &p_sys->lock );