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 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 audioscrobbler_queue_t *p_current_queue, *p_next_queue;
254 input_thread_t *p_input;
255 intf_thread_t *p_intf = (intf_thread_t* ) p_this;
256 intf_sys_t *p_sys = p_intf->p_sys;
257 playlist_t *p_playlist = pl_Yield( p_intf );
259 p_playlist = pl_Yield( p_intf );
260 var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
263 p_input = p_playlist->p_input;
264 if( p_input ) vlc_object_yield( p_input );
269 var_DelCallback( p_input, "state", PlayingChange, p_intf );
272 pl_Release( p_playlist );
274 vlc_mutex_lock ( &p_sys->lock );
275 p_current_queue = p_sys->p_first_queue;
276 vlc_mutex_unlock ( &p_sys->lock );
278 while( ( p_current_queue->i_songs_nb == 10 ) &&
279 ( p_current_queue->p_next_queue != NULL ) )
281 p_next_queue = p_current_queue->p_next_queue;
282 DeleteQueue( p_current_queue );
283 free( p_current_queue );
284 p_current_queue = p_next_queue;
287 DeleteQueue( p_current_queue );
288 free( p_current_queue );
290 vlc_mutex_lock ( &p_sys->lock );
291 if ( p_sys->psz_username )
293 free( p_sys->psz_username );
296 free( p_sys->p_current_song );
297 vlc_mutex_unlock ( &p_sys->lock );
298 vlc_mutex_destroy( &p_sys->lock );
302 /*****************************************************************************
303 * Run : Handshake with audioscrobbler, then submit songs
304 *****************************************************************************/
305 static void Run( intf_thread_t *p_this )
307 char *psz_submit_string = NULL;
311 playlist_t *p_playlist;
312 uint8_t *p_buffer = NULL;
313 char *p_buffer_pos = NULL;
315 audioscrobbler_queue_t *p_first_queue;
318 p_this->p_sys = p_sys_global;
319 p_sys = p_this->p_sys;
320 while( !p_this->b_die )
322 if( p_sys->b_handshaked == VLC_FALSE )
325 ( p_sys->time_last_interval + p_sys->i_interval ) )
327 msg_Dbg( p_this, "Handshaking with last.fm ..." );
328 i_handshake = Handshake( p_this );
330 if( i_handshake == VLC_ENOMEM )
332 msg_Err( p_this, "Out of memory" );
336 else if( i_handshake == VLC_ENOVAR )
337 /* username not set */
339 msg_Dbg( p_this, "Set an username then restart vlc" );
340 vlc_mutex_unlock ( &p_sys->lock );
344 else if( i_handshake == VLC_SUCCESS )
346 msg_Dbg( p_this, "Handshake successfull :)" );
347 vlc_mutex_lock ( &p_sys->lock );
348 p_sys->b_handshaked = VLC_TRUE;
349 vlc_mutex_unlock ( &p_sys->lock );
354 vlc_mutex_lock ( &p_sys->lock );
355 p_sys->i_interval = DEFAULT_INTERVAL;
356 time( &p_sys->time_last_interval );
357 vlc_mutex_unlock ( &p_sys->lock );
364 if ( ( p_sys->p_first_queue->i_songs_nb > 0 ) &&
366 ( p_sys->time_last_interval + p_sys->i_interval ) ) )
368 msg_Dbg( p_this, "Going to submit some data..." );
369 vlc_mutex_lock ( &p_sys->lock );
370 psz_submit_string = malloc( 2048 * sizeof( char ) );
372 if (!psz_submit_string)
374 msg_Err( p_this, "Out of memory" );
375 vlc_mutex_unlock ( &p_sys->lock );
379 for (i_song = 0; i_song < p_sys->p_first_queue->i_songs_nb ;
382 snprintf( psz_submit_string, 2048, POST_DATA,
383 p_sys->psz_username, p_sys->psz_response_md5,
384 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_a,
385 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_t,
386 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_b,
388 i_song, p_sys->p_first_queue->p_queue[i_song]->i_l,
389 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_i
393 p_sys->i_post_socket = net_ConnectTCP( p_this,
394 p_sys->psz_submit_host, p_sys->i_submit_port);
396 i_netprintf = net_Printf(
397 VLC_OBJECT(p_this), p_sys->i_post_socket, NULL,
398 POST_REQUEST, p_sys->psz_submit_file,
399 strlen( psz_submit_string), p_sys->psz_submit_file,
400 VERSION, psz_submit_string
403 if ( i_netprintf == -1 )
405 /* If connection fails, we assume we must handshake again */
406 p_sys->i_interval = DEFAULT_INTERVAL;
407 time( &p_sys->time_last_interval );
408 p_sys->b_handshaked = VLC_FALSE;
409 vlc_mutex_unlock ( &p_sys->lock );
413 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
416 msg_Err( p_this, "Out of memory" );
417 vlc_mutex_unlock ( &p_sys->lock );
421 net_Read( p_this, p_sys->i_post_socket, NULL,
422 p_buffer, 1024, VLC_FALSE );
423 net_Close( p_sys->i_post_socket );
425 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
429 p_sys->i_interval = atoi( p_buffer_pos +
430 strlen( "INTERVAL " ) );
431 time( &p_sys->time_last_interval );
434 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
438 msg_Err( p_this, p_buffer_pos );
439 vlc_mutex_unlock ( &p_sys->lock );
443 p_buffer_pos = strstr( ( char * ) p_buffer, "BADAUTH" );
448 "Authentification failed, handshaking again" );
449 p_sys->b_handshaked = VLC_FALSE;
450 vlc_mutex_unlock ( &p_sys->lock );
454 p_buffer_pos = strstr( ( char * ) p_buffer, "OK" );
458 if ( p_sys->p_first_queue->i_songs_nb == 10 )
460 p_first_queue = p_sys->p_first_queue->p_next_queue;
461 DeleteQueue( p_sys->p_first_queue );
462 free( p_sys->p_first_queue );
463 p_sys->p_first_queue = p_first_queue;
467 DeleteQueue( p_sys->p_first_queue );
468 p_sys->p_first_queue->i_songs_nb = 0;
470 msg_Dbg( p_this, "Submission successfull!" );
472 vlc_mutex_unlock ( &p_sys->lock );
475 msleep( INTF_IDLE_SLEEP );
477 p_playlist = pl_Yield( p_this );
479 if( p_playlist->request.i_status == PLAYLIST_STOPPED )
482 /* if we stopped, we won't submit playing song */
483 vlc_mutex_lock( &p_sys->lock );
484 p_sys->b_queued = VLC_TRUE;
485 p_sys->b_metadata_read = VLC_TRUE;
486 vlc_mutex_unlock( &p_sys->lock );
493 pl_Release( p_playlist );
494 vlc_mutex_lock( &p_sys->lock );
496 if( p_sys->b_metadata_read == VLC_FALSE )
498 time( &played_time );
499 played_time -= p_sys->p_current_song->time_playing;
500 played_time -= p_sys->time_total_pauses;
503 vlc_mutex_unlock( &p_sys->lock );
505 /* ok now we can read meta data */
506 if( played_time > 10 )
508 ReadMetaData( p_this );
513 if( ( p_sys->b_queued == VLC_FALSE )
514 && ( p_sys->b_paused == VLC_FALSE ) )
516 vlc_mutex_unlock( &p_sys->lock );
517 if( AddToQueue( p_this ) == VLC_ENOMEM )
519 msg_Err( p_this, "Out of memory" );
525 vlc_mutex_unlock( &p_sys->lock );
531 /*****************************************************************************
532 * PlayingChange: Playing status change callback
533 *****************************************************************************/
534 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
535 vlc_value_t oldval, vlc_value_t newval, void *p_data )
537 intf_thread_t *p_intf;
540 p_intf = ( intf_thread_t* ) p_data;
541 p_sys = p_intf->p_sys;
543 (void)p_this; (void)psz_var; (void)oldval;
545 vlc_mutex_lock( &p_sys->lock );
547 if( newval.i_int == PAUSE_S )
549 time( &p_sys->time_pause );
550 p_sys->b_paused = VLC_TRUE;
553 if( newval.i_int == PLAYING_S )
555 p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
556 p_sys->b_paused = VLC_TRUE;
559 vlc_mutex_unlock( &p_sys->lock );
564 /*****************************************************************************
565 * ItemChange: Playlist item change callback
566 *****************************************************************************/
567 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
568 vlc_value_t oldval, vlc_value_t newval, void *p_data )
570 playlist_t *p_playlist;
571 input_thread_t *p_input = NULL;
572 intf_thread_t *p_intf;
580 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
582 p_intf = ( intf_thread_t* ) p_data;
583 p_sys = p_intf->p_sys;
585 p_playlist = pl_Yield( p_intf );
587 p_input = p_playlist->p_input;
592 pl_Release( p_playlist );
593 vlc_mutex_lock( &p_sys->lock );
595 /* we won't read p_input */
596 p_sys->b_queued = VLC_TRUE;
597 p_sys->b_metadata_read = VLC_TRUE;
599 vlc_mutex_unlock( &p_sys->lock );
603 vlc_object_yield( p_input );
605 pl_Release( p_playlist );
607 var_AddCallback( p_input, "state", PlayingChange, p_intf );
609 vlc_mutex_lock ( &p_sys->lock );
611 /* reset pause counter */
612 p_sys->time_total_pauses = 0;
614 /* we save the p_input value to delete the callback later */
615 p_sys->p_input = p_input;
617 /* we'll read after to be sure it's present */
618 p_sys->b_metadata_read = VLC_FALSE;
620 p_sys->b_queued = VLC_TRUE;
623 epoch_tm = gmtime( &epoch );
624 snprintf( psz_date, 20, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
625 epoch_tm->tm_year+1900, epoch_tm->tm_mon+1, epoch_tm->tm_mday,
626 epoch_tm->tm_hour, epoch_tm->tm_min, epoch_tm->tm_sec );
628 p_sys->p_current_song->psz_i = encode_URI_component( psz_date );
629 p_sys->p_current_song->time_playing = epoch;
631 p_sys->b_paused = ( p_input->b_dead || !p_input->input.p_item->psz_name )
632 ? VLC_TRUE : VLC_FALSE;
634 vlc_mutex_unlock( &p_sys->lock );
636 vlc_object_release( p_input );
640 /*****************************************************************************
641 * AddToQueue: Add the played song to the queue to be submitted
642 *****************************************************************************/
643 static int AddToQueue ( intf_thread_t *p_this )
647 audioscrobbler_queue_t *p_queue = NULL, *p_next_queue = NULL;
650 p_sys = p_this->p_sys;
652 /* has it been queued already ? is it long enough ? */
653 vlc_mutex_lock( &p_sys->lock );
654 if( p_sys->p_current_song->i_l < 30 )
656 msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
657 p_sys->b_queued = VLC_TRUE;
658 vlc_mutex_unlock ( &p_sys->lock );
662 /* wait for the user to listen enough before submitting */
663 time ( &played_time );
664 played_time -= p_sys->p_current_song->time_playing;
665 played_time -= p_sys->time_total_pauses;
667 if( ( played_time < 240 )
668 && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
670 vlc_mutex_unlock ( &p_sys->lock );
674 msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
676 p_queue = p_sys->p_first_queue;
678 while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
680 p_queue = p_queue->p_next_queue;
683 i_songs_nb = p_queue->i_songs_nb;
685 if( i_songs_nb == 10 )
687 p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
690 vlc_mutex_unlock ( &p_sys->lock );
693 p_queue->p_next_queue = p_next_queue;
695 p_queue = p_next_queue;
696 p_queue->i_songs_nb = i_songs_nb;
699 p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
701 p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
703 p_queue->p_queue[i_songs_nb]->psz_a =
704 strdup( p_sys->p_current_song->psz_a );
706 p_queue->p_queue[i_songs_nb]->psz_t =
707 strdup( p_sys->p_current_song->psz_t );
709 p_queue->p_queue[i_songs_nb]->psz_b =
710 strdup( p_sys->p_current_song->psz_b );
712 p_queue->p_queue[i_songs_nb]->psz_i =
713 strdup( p_sys->p_current_song->psz_i );
715 p_queue->i_songs_nb++;
716 p_sys->b_queued = VLC_TRUE;
718 vlc_mutex_unlock( &p_sys->lock );
723 /*****************************************************************************
724 * Handshake : Init audioscrobbler connection
725 *****************************************************************************/
726 static int Handshake( intf_thread_t *p_this )
728 char *psz_password = NULL;
729 struct md5_s *p_struct_md5 = NULL;
730 char *psz_password_md5 = NULL;
731 char *ps_challenge_md5 = NULL;
734 char *psz_handshake_url = NULL;
736 uint8_t *p_buffer = NULL;
737 char *p_buffer_pos = NULL;
738 char *psz_buffer_substring = NULL;
739 char *psz_url_parser = NULL;
742 char *b1, *b2, *b3, *b4;
744 intf_thread_t *p_intf;
747 p_intf = ( intf_thread_t* ) p_this;
748 p_sys = p_this->p_sys;
750 vlc_mutex_lock ( &p_sys->lock );
752 p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
753 if ( !p_sys->psz_username )
759 if ( !*p_sys->psz_username )
761 msg_Info( p_this, "You have to set an username! "
762 "Visit https://www.last.fm/join/" );
766 psz_handshake_url = malloc( 1024 );
767 if ( !psz_handshake_url )
772 snprintf( psz_handshake_url, 1024,
773 "http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s",
774 CLIENT_NAME, CLIENT_VERSION, p_sys->psz_username );
776 p_stream = stream_UrlNew( p_intf, psz_handshake_url);
778 free( psz_handshake_url );
782 p_sys->i_interval = DEFAULT_INTERVAL;
783 time( &p_sys->time_last_interval );
784 vlc_mutex_unlock ( &p_sys->lock );
788 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
791 stream_Delete( p_stream );
795 if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
797 stream_Delete( p_stream );
799 vlc_mutex_unlock ( &p_sys->lock );
803 stream_Delete( p_stream );
805 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
809 p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
810 time( &p_sys->time_last_interval );
813 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
817 msg_Info( p_this, p_buffer_pos );
819 vlc_mutex_unlock ( &p_sys->lock );
823 p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
827 msg_Info( p_intf, "Username is incorrect" );
829 vlc_mutex_unlock ( &p_sys->lock );
833 p_buffer_pos = strstr( ( char * ) p_buffer, "UPDATE" );
837 msg_Dbg( p_intf, "Protocol updated" );
838 msg_Dbg( p_intf, p_buffer_pos );
843 p_buffer_pos = strstr( ( char * ) p_buffer, "UPTODATE" );
846 msg_Dbg( p_intf, "Protocol error" );
848 vlc_mutex_unlock ( &p_sys->lock );
853 psz_buffer_substring = strndup( strstr( p_buffer_pos, "\n" ) + 1, 32 );
854 if ( !psz_buffer_substring )
860 ps_challenge_md5 = malloc( sizeof( char ) * 32 );
861 if ( !ps_challenge_md5 )
865 memcpy( ps_challenge_md5, psz_buffer_substring, 32 );
866 free( psz_buffer_substring );
869 p_buffer_pos = ( void* ) strstr( ( char* ) p_buffer, "http://" );
871 if ( p_sys->psz_submit_host != NULL )
873 free( p_sys->psz_submit_host );
876 if ( p_sys->psz_submit_file != NULL )
878 free( p_sys->psz_submit_file );
881 psz_url_parser = p_buffer_pos + strlen( "http://" );
883 i_url_pos = strcspn( psz_url_parser, ":" );
884 p_sys->psz_submit_host = strndup( psz_url_parser, i_url_pos );
886 p_sys->i_submit_port = atoi( psz_url_parser + i_url_pos + 1 );
888 psz_url_parser += strcspn( psz_url_parser , "/" ) + 1;
889 i_url_pos = strcspn( psz_url_parser, "\n" );
890 p_sys->psz_submit_file = strndup( psz_url_parser, i_url_pos );
894 p_struct_md5 = malloc( sizeof( struct md5_s ) );
900 psz_password = config_GetPsz(p_this, "lastfm-password");
906 InitMD5( p_struct_md5 );
907 AddMD5( p_struct_md5, ( uint8_t* ) psz_password, strlen( psz_password ) );
908 EndMD5( p_struct_md5 );
910 free( psz_password );
912 psz_password_md5 = malloc ( 33 * sizeof( char ) );
913 if ( !psz_password_md5 )
918 for ( i = 0; i < 4; i++ )
920 /* TODO check that this works on every arch/platform (uint32_t to char) */
921 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
922 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
923 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
924 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
925 sprintf( &psz_password_md5[8*i], "%s%s%s%s", b1, b2, b3, b4 );
932 strlwr( psz_password_md5 );
934 InitMD5( p_struct_md5 );
935 AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
936 AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
937 EndMD5( p_struct_md5 );
939 free( ps_challenge_md5 );
940 free( psz_password_md5 );
942 for ( i = 0; i < 4; i++ )
944 b1 = hexa( p_struct_md5->p_digest[i] % 256 );
945 b2 = hexa( ( p_struct_md5->p_digest[i] / 256 ) % 256 );
946 b3 = hexa( ( p_struct_md5->p_digest[i] / 65536 ) % 256 );
947 b4 = hexa( p_struct_md5->p_digest[i] / 16777216 );
948 sprintf( &p_sys->psz_response_md5[8*i],"%s%s%s%s", b1, b2, b3, b4 );
955 p_sys->psz_response_md5[32] = 0;
957 strlwr( p_sys->psz_response_md5 );
959 vlc_mutex_unlock ( &p_sys->lock );
965 free( p_struct_md5 );
966 free( psz_buffer_substring );
968 vlc_mutex_unlock ( &p_sys->lock );
972 /*****************************************************************************
973 * hexa : Converts a byte to a string in its hexadecimal value
974 *****************************************************************************/
975 char *hexa( short int i )
977 char *res = calloc( 3 , sizeof( char ) );
979 ((i/16) < 10) ? res[0] = (i / 16) + '0' : ( res[0] = (i/16) + 'a' - 10 );
980 ((i%16) < 10) ? res[1] = (i % 16) + '0' : ( res[1] = (i%16) + 'a' - 10 );
984 /*****************************************************************************
985 * strlwr : Converts a string to lower case
986 *****************************************************************************/
987 #if !defined(strlwr) && !defined( WIN32 )
988 char* strlwr(char *psz_string)
990 while ( *psz_string )
992 *psz_string++ = tolower( *psz_string );
998 /*****************************************************************************
999 * DeleteQueue : Free all songs from an audioscrobbler_queue_t
1000 *****************************************************************************/
1001 void DeleteQueue( audioscrobbler_queue_t *p_queue )
1005 for( i = 0; i < p_queue->i_songs_nb; i++ )
1007 free( p_queue->p_queue[i]->psz_a );
1008 free( p_queue->p_queue[i]->psz_b );
1009 free( p_queue->p_queue[i]->psz_t );
1010 free( p_queue->p_queue[i]->psz_i );
1011 free( p_queue->p_queue[i] );
1015 /*****************************************************************************
1016 * ReadMetaData : Puts current song's meta data in p_sys->p_current_song
1017 *****************************************************************************/
1018 static int ReadMetaData( intf_thread_t *p_this )
1020 playlist_t *p_playlist;
1021 char *psz_title = NULL;
1022 char *psz_artist = NULL;
1023 char *psz_album = NULL;
1025 input_thread_t *p_input = NULL;
1026 vlc_value_t video_val;
1029 p_sys = p_this->p_sys;
1030 p_playlist = pl_Yield( p_this );
1032 p_input = p_playlist->p_input;
1039 vlc_object_yield( p_input );
1041 pl_Release( p_playlist );
1043 if ( p_input->input.p_item->i_type == ITEM_TYPE_NET )
1045 msg_Dbg( p_this, "We play a stream -> no submission");
1049 var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
1050 if( video_val.i_int > 0 )
1052 msg_Dbg( p_this, "We play a video -> no submission");
1056 if ( p_input->input.p_item->p_meta->psz_artist )
1058 psz_artist = encode_URI_component(
1059 p_input->input.p_item->p_meta->psz_artist );
1067 msg_Dbg( p_this, "No artist.." );
1071 if ( p_input->input.p_item->p_meta->psz_album )
1073 psz_album = encode_URI_component(
1074 p_input->input.p_item->p_meta->psz_album );
1082 msg_Dbg( p_this, "No album.." );
1086 if ( p_input->input.p_item->psz_name )
1088 psz_title = encode_URI_component( p_input->input.p_item->psz_name );
1096 msg_Dbg( p_this, "No track name.." );
1100 i_length = p_input->input.p_item->i_duration / 1000000;
1102 vlc_object_release( p_input );
1104 vlc_mutex_lock ( &p_sys->lock );
1106 p_sys->p_current_song->psz_a = strdup( psz_artist );
1107 p_sys->p_current_song->psz_t = strdup( psz_title );
1108 p_sys->p_current_song->psz_b = strdup( psz_album );
1109 p_sys->p_current_song->i_l = i_length;
1110 p_sys->b_queued = VLC_FALSE;
1111 p_sys->b_metadata_read = VLC_TRUE;
1113 vlc_mutex_unlock ( &p_sys->lock );
1115 msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1130 vlc_object_release( p_input );
1136 vlc_mutex_lock( &p_sys->lock );
1137 p_sys->b_queued = VLC_TRUE;
1138 p_sys->b_metadata_read = VLC_TRUE;
1139 vlc_mutex_unlock( &p_sys->lock );