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 )
41 * implement musicbrainz unique track identifier in p_meta
42 * check meta_engine's state, and remove delaying of metadata reading
43 * check md5 operations on BIGENDIAN and 64 bits architectures
49 #include <vlc_block.h>
50 #include <vlc_stream.h>
53 #include <vlc_interaction.h>
55 /*****************************************************************************
57 *****************************************************************************/
59 /* Keeps track of metadata to be submitted, and if song has been submitted */
60 typedef struct audioscrobbler_song_t
62 char *psz_a; /* track artist */
63 char *psz_t; /* track title */
64 char *psz_b; /* track album */
65 int i_l; /* track length */
66 /* vlc can't retrieve musicbrainz id, so let's ignore it */
67 /* int i_m; */ /* musicbrainz id */
68 char *psz_i; /* date */
69 time_t time_playing; /* date (epoch) */
70 } audioscrobbler_song_t;
73 /* Queue to be submitted to server, 10 songs max */
74 typedef struct audioscrobbler_queue_t
76 audioscrobbler_song_t **p_queue; /* contains up to 10 songs */
77 int i_songs_nb; /* number of songs */
78 void *p_next_queue; /* if queue full, pointer to next */
79 } audioscrobbler_queue_t;
83 audioscrobbler_queue_t *p_first_queue; /* 1st queue */
84 vlc_mutex_t lock; /* p_sys mutex */
86 /* data about audioscrobbler session */
87 int i_interval; /* last interval recorded */
88 time_t time_last_interval; /* when was it recorded ? */
89 char *psz_submit_host; /* where to submit data ? */
90 int i_submit_port; /* at which port ? */
91 char *psz_submit_file; /* in which file ? */
92 char *psz_username; /* last.fm username */
93 vlc_bool_t b_handshaked; /* did we handshake ? */
94 int i_post_socket; /* socket for submission */
95 char *psz_response_md5; /* md5 response to use */
97 /* data about input elements */
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 ? */
104 vlc_bool_t b_waiting_meta; /* we need fetched data? */
107 intf_sys_t *p_sys_global; /* for use same p_sys in all threads */
109 static int Open ( vlc_object_t * );
110 static void Close ( vlc_object_t * );
111 static void Run ( intf_thread_t * );
112 static int ItemChange ( vlc_object_t *, const char *,
113 vlc_value_t, vlc_value_t, void * );
114 static int PlayingChange( vlc_object_t *, const char *,
115 vlc_value_t, vlc_value_t, void * );
116 static int AddToQueue ( intf_thread_t *p_this );
117 static int Handshake ( intf_thread_t *p_sd );
118 static int ReadMetaData ( intf_thread_t *p_this );
119 static int ReadLocalMetaData( intf_thread_t *p_this, input_thread_t *p_input );
120 void DeleteQueue( audioscrobbler_queue_t *p_queue );
122 #if !defined(strlwr) && !defined( WIN32 )
123 char* strlwr(char *psz_string);
126 /*****************************************************************************
128 ****************************************************************************/
131 #define APPLICATION_NAME "VLC media player"
132 #define USERNAME_TEXT N_("Username")
133 #define USERNAME_LONGTEXT N_("The username of your last.fm account")
134 #define PASSWORD_TEXT N_("Password")
135 #define PASSWORD_LONGTEXT N_("The password of your last.fm account")
136 /* if something goes wrong, we wait at least one minute before trying again */
137 #define DEFAULT_INTERVAL 60
138 #define CLIENT_NAME PACKAGE
139 #define CLIENT_VERSION VERSION
141 /* HTTP POST request : to submit data */
142 #define POST_REQUEST "POST /%s HTTP/1.1\n" \
143 "Accept-Encoding: identity\n" \
144 "Content-length: %d\n" \
145 "Connection: close\n" \
146 "Content-type: application/x-www-form-urlencoded\n" \
148 "User-agent: VLC Media Player/%s\r\n" \
154 #define POST_DATA "u=%s&s=%s&a%%5B%d%%5D=%s&t%%5B%d%%5D=%s" \
155 "&b%%5B%d%%5D=%s&m%%5B%d%%5D=&l%%5B%d%%5D=%d&i%%5B%d%%5D=%s"
158 set_category( CAT_INTERFACE );
159 set_subcategory( SUBCAT_INTERFACE_CONTROL );
160 set_shortname( N_( "Audioscrobbler" ) );
161 set_description( N_("Audioscrobbler submission Plugin") );
162 add_string( "lastfm-username", "", NULL,
163 USERNAME_TEXT, USERNAME_LONGTEXT, VLC_FALSE );
164 add_string( "lastfm-password", "", NULL,
165 PASSWORD_TEXT, PASSWORD_LONGTEXT, VLC_FALSE );
166 set_capability( "interface", 0 );
167 set_callbacks( Open, Close );
170 /*****************************************************************************
171 * Open: initialize and create stuff
172 *****************************************************************************/
174 static int Open( vlc_object_t *p_this )
176 playlist_t *p_playlist;
178 intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
179 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
184 vlc_mutex_init( p_this, &p_sys->lock );
186 p_sys_global = p_sys;
187 p_sys->psz_submit_host = NULL;
188 p_sys->psz_submit_file = NULL;
189 p_sys->b_handshaked = VLC_FALSE;
190 p_sys->i_interval = 0;
191 p_sys->time_last_interval = time( NULL );
192 p_sys->psz_username = NULL;
193 p_sys->b_paused = VLC_FALSE;
195 /* md5 response is 32 chars, + final \0 */
196 p_sys->psz_response_md5 = malloc( sizeof( char ) * 33 );
197 if( !p_sys->psz_response_md5 )
199 vlc_mutex_destroy ( &p_sys->lock );
203 p_sys->p_first_queue = malloc( sizeof( audioscrobbler_queue_t ) );
204 if( !p_sys->p_first_queue )
206 vlc_mutex_destroy( &p_sys->lock );
210 p_sys->p_current_song = malloc( sizeof( audioscrobbler_song_t ) );
211 if( !p_sys->p_current_song )
213 vlc_mutex_destroy( &p_sys->lock );
217 /* queues can't contain more than 10 songs */
218 p_sys->p_first_queue->p_queue =
219 malloc( 10 * sizeof( audioscrobbler_song_t ) );
220 if( !p_sys->p_current_song )
222 vlc_mutex_destroy( &p_sys->lock );
226 p_sys->p_first_queue->i_songs_nb = 0;
227 p_sys->p_first_queue->p_next_queue = NULL;
229 p_playlist = pl_Yield( p_intf );
230 var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
231 pl_Release( p_playlist );
233 p_intf->pf_run = Run;
238 free( p_sys->p_current_song );
239 free( p_sys->p_first_queue );
240 free( p_sys->psz_response_md5 );
246 /*****************************************************************************
247 * Close: destroy interface stuff
248 *****************************************************************************/
249 static void Close( vlc_object_t *p_this )
251 audioscrobbler_queue_t *p_current_queue, *p_next_queue;
252 playlist_t *p_playlist;
253 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;
258 p_playlist = pl_Yield( p_intf );
260 p_input = p_playlist->p_input;
261 var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
265 vlc_object_yield( p_input );
266 var_DelCallback( p_input, "state", PlayingChange, p_intf );
267 vlc_object_release( p_input );
271 pl_Release( p_playlist );
273 vlc_mutex_lock ( &p_sys->lock );
274 p_current_queue = p_sys->p_first_queue;
275 vlc_mutex_unlock ( &p_sys->lock );
277 while( ( p_current_queue->i_songs_nb == 10 ) &&
278 ( p_current_queue->p_next_queue != NULL ) )
280 p_next_queue = p_current_queue->p_next_queue;
281 DeleteQueue( p_current_queue );
282 free( p_current_queue );
283 p_current_queue = p_next_queue;
286 DeleteQueue( p_current_queue );
287 free( p_current_queue );
289 vlc_mutex_lock ( &p_sys->lock );
290 if ( p_sys->psz_username )
292 free( p_sys->psz_username );
295 free( p_sys->p_current_song );
296 vlc_mutex_unlock ( &p_sys->lock );
297 vlc_mutex_destroy( &p_sys->lock );
301 /*****************************************************************************
302 * Run : Handshake with audioscrobbler, then submit songs
303 *****************************************************************************/
304 static void Run( intf_thread_t *p_this )
306 char *psz_submit_string = NULL;
310 playlist_t *p_playlist;
311 uint8_t *p_buffer = NULL;
312 char *p_buffer_pos = NULL;
313 audioscrobbler_queue_t *p_first_queue;
314 /* TODO: remove when meta_engine works */
317 p_this->p_sys = p_sys_global;
318 intf_sys_t *p_sys = p_this->p_sys;
320 while( !p_this->b_die )
323 if ( ( p_sys->p_first_queue->i_songs_nb > 0 ) &&
325 ( p_sys->time_last_interval + p_sys->i_interval ) ) )
328 if( p_sys->b_handshaked == VLC_FALSE )
331 ( p_sys->time_last_interval + p_sys->i_interval ) )
333 msg_Dbg( p_this, "Handshaking with last.fm ..." );
334 i_handshake = Handshake( p_this );
336 if( i_handshake == VLC_ENOMEM )
338 msg_Err( p_this, "Out of memory" );
342 else if( i_handshake == VLC_ENOVAR )
343 /* username not set */
345 vlc_mutex_unlock ( &p_sys->lock );
346 intf_UserFatal( p_this, VLC_FALSE,
347 _("last.fm username not set"),
348 _("You have to set a username,"
349 " and then restart VLC.\n"
350 "Visit https://www.last.fm/join/"
351 " if you don't have one.")
356 else if( i_handshake == VLC_SUCCESS )
358 msg_Dbg( p_this, "Handshake successfull :)" );
359 vlc_mutex_lock ( &p_sys->lock );
360 p_sys->b_handshaked = VLC_TRUE;
361 vlc_mutex_unlock ( &p_sys->lock );
365 /* VLC_GENERIC : we'll try later */
367 vlc_mutex_lock ( &p_sys->lock );
368 p_sys->i_interval = DEFAULT_INTERVAL;
369 time( &p_sys->time_last_interval );
370 vlc_mutex_unlock ( &p_sys->lock );
376 msg_Dbg( p_this, "Going to submit some data..." );
377 vlc_mutex_lock ( &p_sys->lock );
378 psz_submit_string = malloc( 2048 * sizeof( char ) );
380 if (!psz_submit_string)
382 msg_Err( p_this, "Out of memory" );
383 vlc_mutex_unlock ( &p_sys->lock );
387 for (i_song = 0; i_song < p_sys->p_first_queue->i_songs_nb ;
390 snprintf( psz_submit_string, 2048, POST_DATA,
391 p_sys->psz_username, p_sys->psz_response_md5,
392 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_a,
393 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_t,
394 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_b,
396 i_song, p_sys->p_first_queue->p_queue[i_song]->i_l,
397 i_song, p_sys->p_first_queue->p_queue[i_song]->psz_i
401 p_sys->i_post_socket = net_ConnectTCP( p_this,
402 p_sys->psz_submit_host, p_sys->i_submit_port);
404 i_netprintf = net_Printf(
405 VLC_OBJECT(p_this), p_sys->i_post_socket, NULL,
406 POST_REQUEST, p_sys->psz_submit_file,
407 strlen( psz_submit_string), p_sys->psz_submit_file,
408 VERSION, psz_submit_string
411 if ( i_netprintf == -1 )
413 /* If connection fails, we assume we must handshake again */
414 p_sys->i_interval = DEFAULT_INTERVAL;
415 time( &p_sys->time_last_interval );
416 p_sys->b_handshaked = VLC_FALSE;
417 vlc_mutex_unlock ( &p_sys->lock );
421 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
424 msg_Err( p_this, "Out of memory" );
425 vlc_mutex_unlock ( &p_sys->lock );
429 net_Read( p_this, p_sys->i_post_socket, NULL,
430 p_buffer, 1024, VLC_FALSE );
431 net_Close( p_sys->i_post_socket );
433 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
437 p_sys->i_interval = atoi( p_buffer_pos +
438 strlen( "INTERVAL " ) );
439 time( &p_sys->time_last_interval );
442 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
446 msg_Dbg( p_this, p_buffer_pos );
447 vlc_mutex_unlock ( &p_sys->lock );
451 p_buffer_pos = strstr( ( char * ) p_buffer, "BADAUTH" );
455 msg_Dbg( p_this, "Authentification failed, handshaking again" );
456 p_sys->b_handshaked = VLC_FALSE;
457 vlc_mutex_unlock ( &p_sys->lock );
461 p_buffer_pos = strstr( ( char * ) p_buffer, "OK" );
465 if ( p_sys->p_first_queue->i_songs_nb == 10 )
467 p_first_queue = p_sys->p_first_queue->p_next_queue;
468 DeleteQueue( p_sys->p_first_queue );
469 free( p_sys->p_first_queue );
470 p_sys->p_first_queue = p_first_queue;
474 DeleteQueue( p_sys->p_first_queue );
475 p_sys->p_first_queue->i_songs_nb = 0;
477 msg_Dbg( p_this, "Submission successfull!" );
479 vlc_mutex_unlock ( &p_sys->lock );
482 msleep( INTF_IDLE_SLEEP );
484 p_playlist = pl_Yield( p_this );
486 if( p_playlist->request.i_status == PLAYLIST_STOPPED )
489 pl_Release( p_playlist );
490 /* if we stopped, we won't submit playing song */
491 vlc_mutex_lock( &p_sys->lock );
492 p_sys->b_queued = VLC_TRUE;
493 p_sys->b_metadata_read = VLC_TRUE;
494 vlc_mutex_unlock( &p_sys->lock );
499 pl_Release( p_playlist );
502 vlc_mutex_lock( &p_sys->lock );
503 if( p_sys->b_metadata_read == VLC_FALSE )
505 /* TODO: remove when meta_engine works */
506 time( &played_time );
507 played_time -= p_sys->p_current_song->time_playing;
508 played_time -= p_sys->time_total_pauses;
510 vlc_mutex_unlock( &p_sys->lock );
512 /* TODO: remove when meta_engine works */
513 if( played_time > 10 )
515 if ( ReadMetaData( p_this ) == VLC_ENOMEM )
517 msg_Err( p_this, "Out of memory" );
524 if( ( p_sys->b_queued == VLC_FALSE )
525 && ( p_sys->b_paused == VLC_FALSE ) )
527 vlc_mutex_unlock( &p_sys->lock );
528 if( AddToQueue( p_this ) == VLC_ENOMEM )
530 msg_Err( p_this, "Out of memory" );
536 vlc_mutex_unlock( &p_sys->lock );
542 /*****************************************************************************
543 * PlayingChange: Playing status change callback
544 *****************************************************************************/
545 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
546 vlc_value_t oldval, vlc_value_t newval, void *p_data )
548 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
549 intf_sys_t *p_sys = p_intf->p_sys;
551 (void)p_this; (void)psz_var; (void)oldval;
553 vlc_mutex_lock( &p_sys->lock );
555 if( newval.i_int == PAUSE_S )
557 time( &p_sys->time_pause );
558 p_sys->b_paused = VLC_TRUE;
561 if( newval.i_int == PLAYING_S )
563 p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
564 p_sys->b_paused = VLC_FALSE;
567 vlc_mutex_unlock( &p_sys->lock );
572 /*****************************************************************************
573 * ItemChange: Playlist item change callback
574 *****************************************************************************/
575 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
576 vlc_value_t oldval, vlc_value_t newval, void *p_data )
578 playlist_t *p_playlist;
579 input_thread_t *p_input = NULL;
584 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
586 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
587 intf_sys_t *p_sys = p_intf->p_sys;
589 p_playlist = pl_Yield( p_intf );
591 p_input = p_playlist->p_input;
596 pl_Release( p_playlist );
597 vlc_mutex_lock( &p_sys->lock );
599 /* we won't read p_input */
600 p_sys->b_queued = VLC_TRUE;
601 p_sys->b_metadata_read = VLC_TRUE;
603 vlc_mutex_unlock( &p_sys->lock );
607 vlc_object_yield( p_input );
609 pl_Release( p_playlist );
611 var_AddCallback( p_input, "state", PlayingChange, p_intf );
613 vlc_mutex_lock ( &p_sys->lock );
615 /* reset pause counter */
616 p_sys->time_total_pauses = 0;
618 /* we'll read metadata when it's present */
619 p_sys->b_metadata_read = VLC_FALSE;
620 p_sys->b_waiting_meta = 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;
650 intf_sys_t *p_sys = p_this->p_sys;
652 /* wait for the user to listen enough before submitting */
653 time ( &played_time );
654 played_time -= p_sys->p_current_song->time_playing;
655 played_time -= p_sys->time_total_pauses;
657 vlc_mutex_lock( &p_sys->lock );
658 if( ( played_time < 240 )
659 && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
661 vlc_mutex_unlock ( &p_sys->lock );
665 if( p_sys->p_current_song->i_l < 30 )
667 msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
668 p_sys->b_queued = VLC_TRUE;
669 vlc_mutex_unlock ( &p_sys->lock );
673 if( !*p_sys->p_current_song->psz_a || !*p_sys->p_current_song->psz_t )
675 msg_Dbg( p_this, "Missing artist or title -> not submitting" );
676 p_sys->b_queued = VLC_TRUE;
677 vlc_mutex_unlock ( &p_sys->lock );
681 msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
683 /* go to last queue */
684 p_queue = p_sys->p_first_queue;
685 while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
687 p_queue = p_queue->p_next_queue;
690 i_songs_nb = p_queue->i_songs_nb;
692 if( i_songs_nb == 10 )
694 p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
697 vlc_mutex_unlock ( &p_sys->lock );
700 p_queue->p_next_queue = p_next_queue;
702 p_queue = p_next_queue;
703 p_queue->i_songs_nb = i_songs_nb;
706 p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
708 p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
710 p_queue->p_queue[i_songs_nb]->psz_a =
711 strdup( p_sys->p_current_song->psz_a );
713 p_queue->p_queue[i_songs_nb]->psz_t =
714 strdup( p_sys->p_current_song->psz_t );
716 p_queue->p_queue[i_songs_nb]->psz_b =
717 strdup( p_sys->p_current_song->psz_b );
719 p_queue->p_queue[i_songs_nb]->psz_i =
720 strdup( p_sys->p_current_song->psz_i );
722 p_queue->i_songs_nb++;
723 p_sys->b_queued = VLC_TRUE;
725 vlc_mutex_unlock( &p_sys->lock );
730 /*****************************************************************************
731 * Handshake : Init audioscrobbler connection
732 *****************************************************************************/
733 static int Handshake( intf_thread_t *p_this )
735 char *psz_password = NULL;
736 struct md5_s *p_struct_md5 = NULL;
737 char *psz_password_md5 = NULL;
738 char *ps_challenge_md5 = NULL;
741 char *psz_handshake_url = NULL;
743 uint8_t *p_buffer = NULL;
744 char *p_buffer_pos = NULL;
745 char *psz_buffer_substring = NULL;
746 char *psz_url_parser = NULL;
749 intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
750 intf_sys_t *p_sys = p_this->p_sys;
752 vlc_mutex_lock ( &p_sys->lock );
754 p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
755 if ( !p_sys->psz_username )
761 if ( !*p_sys->psz_username )
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 vlc_mutex_unlock ( &p_sys->lock );
786 p_buffer = ( uint8_t* ) calloc( 1, 1024 );
789 stream_Delete( p_stream );
793 if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
795 stream_Delete( p_stream );
797 vlc_mutex_unlock ( &p_sys->lock );
801 stream_Delete( p_stream );
803 p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
807 p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
808 time( &p_sys->time_last_interval );
811 p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
815 msg_Dbg( p_this, p_buffer_pos );
817 vlc_mutex_unlock ( &p_sys->lock );
821 p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
825 intf_UserFatal( p_this, VLC_FALSE, _("Bad last.fm Username"),
826 _("last.fm username is incorrect, please verify your settings")
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 sprintf( &psz_password_md5[8*i], "%02x%02x%02x%02x",
921 p_struct_md5->p_digest[i] & 0xff,
922 ( p_struct_md5->p_digest[i] >> 8 ) & 0xff,
923 ( p_struct_md5->p_digest[i] >> 16 ) & 0xff,
924 p_struct_md5->p_digest[i] >> 24
928 strlwr( psz_password_md5 );
930 InitMD5( p_struct_md5 );
931 AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
932 AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
933 EndMD5( p_struct_md5 );
935 free( ps_challenge_md5 );
936 free( psz_password_md5 );
938 for ( i = 0; i < 4; i++ )
940 sprintf( &p_sys->psz_response_md5[8*i], "%02x%02x%02x%02x",
941 p_struct_md5->p_digest[i] & 0xff,
942 ( p_struct_md5->p_digest[i] >> 8 ) & 0xff,
943 ( p_struct_md5->p_digest[i] >> 16 ) & 0xff,
944 p_struct_md5->p_digest[i] >> 24
948 p_sys->psz_response_md5[32] = 0;
950 strlwr( p_sys->psz_response_md5 );
952 vlc_mutex_unlock ( &p_sys->lock );
958 free( p_struct_md5 );
959 free( psz_buffer_substring );
961 vlc_mutex_unlock ( &p_sys->lock );
965 /*****************************************************************************
966 * strlwr : Converts a string to lower case
967 *****************************************************************************/
968 #if !defined(strlwr) && !defined( WIN32 )
969 char* strlwr(char *psz_string)
971 while ( *psz_string )
973 *psz_string++ = tolower( *psz_string );
979 /*****************************************************************************
980 * DeleteQueue : Free all songs from an audioscrobbler_queue_t
981 *****************************************************************************/
982 void DeleteQueue( audioscrobbler_queue_t *p_queue )
986 for( i = 0; i < p_queue->i_songs_nb; i++ )
988 free( p_queue->p_queue[i]->psz_a );
989 free( p_queue->p_queue[i]->psz_b );
990 free( p_queue->p_queue[i]->psz_t );
991 free( p_queue->p_queue[i]->psz_i );
992 free( p_queue->p_queue[i] );
996 /*****************************************************************************
997 * ReadMetaData : Read meta data when parsed by vlc
998 * or wait for fetching if unavailable
999 *****************************************************************************/
1000 static int ReadMetaData( intf_thread_t *p_this )
1002 playlist_t *p_playlist;
1003 input_thread_t *p_input = NULL;
1004 vlc_value_t video_val;
1006 intf_sys_t *p_sys = p_this->p_sys;
1008 p_playlist = pl_Yield( p_this );
1010 p_input = p_playlist->p_input;
1015 pl_Release( p_playlist );
1016 return( VLC_SUCCESS );
1019 vlc_object_yield( p_input );
1021 pl_Release( p_playlist );
1023 var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
1024 if( ( video_val.i_int > 0 ) || \
1025 ( p_input->input.p_item->i_type == ITEM_TYPE_NET ) )
1027 msg_Dbg( p_this, "Not an audio file -> no submission");
1028 vlc_object_release( p_input );
1030 vlc_mutex_lock( &p_sys->lock );
1031 p_sys->b_queued = VLC_TRUE;
1032 p_sys->b_metadata_read = VLC_TRUE;
1033 vlc_mutex_unlock( &p_sys->lock );
1038 return ReadLocalMetaData( p_this, p_input );
1041 /*****************************************************************************
1042 * ReadLocalMetaData : Puts current song's meta data in p_sys->p_current_song
1043 *****************************************************************************/
1044 static int ReadLocalMetaData( intf_thread_t *p_this, input_thread_t *p_input )
1046 char *psz_title = NULL;
1047 char *psz_artist = NULL;
1048 char *psz_album = NULL;
1050 vlc_bool_t b_waiting;
1053 intf_sys_t *p_sys = p_this->p_sys;
1055 i_status = p_input->input.p_item->p_meta->i_status;
1057 vlc_mutex_lock( &p_sys->lock );
1058 b_waiting = p_sys->b_waiting_meta;
1059 vlc_mutex_unlock( &p_sys->lock );
1061 /* TODO : remove if (1) when meta_engine works */
1062 if ( (1/*( i_status & ITEM_PREPARSED )*/&& ( b_waiting == VLC_FALSE ) ) || \
1063 ( ( i_status & ITEM_META_FETCHED ) && ( b_waiting == VLC_TRUE ) ) )
1065 if ( p_input->input.p_item->p_meta->psz_artist )
1067 psz_artist = encode_URI_component(
1068 p_input->input.p_item->p_meta->psz_artist );
1076 msg_Dbg( p_this, "No artist.." );
1077 if ( b_waiting == VLC_TRUE )
1079 psz_artist = calloc( 1, sizeof( char ) );
1083 goto waiting_meta_data_fetching;
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.." );
1098 if ( b_waiting == VLC_TRUE )
1100 psz_title = calloc( 1, sizeof( char ) );
1104 goto waiting_meta_data_fetching;
1108 if ( p_input->input.p_item->p_meta->psz_album )
1110 psz_album = encode_URI_component(
1111 p_input->input.p_item->p_meta->psz_album );
1119 psz_album = calloc( 1, sizeof( char ) );
1122 i_length = p_input->input.p_item->i_duration / 1000000;
1124 vlc_object_release( p_input );
1126 vlc_mutex_lock ( &p_sys->lock );
1128 p_sys->p_current_song->psz_a = strdup( psz_artist );
1129 p_sys->p_current_song->psz_t = strdup( psz_title );
1130 p_sys->p_current_song->psz_b = strdup( psz_album );
1131 p_sys->p_current_song->i_l = i_length;
1132 p_sys->b_queued = VLC_FALSE;
1133 p_sys->b_metadata_read = VLC_TRUE;
1135 vlc_mutex_unlock ( &p_sys->lock );
1137 msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1148 waiting_meta_data_fetching:
1149 vlc_object_release( p_input );
1151 vlc_mutex_lock( &p_sys->lock );
1152 p_sys->b_waiting_meta = VLC_TRUE;
1153 vlc_mutex_unlock( &p_sys->lock );
1162 vlc_object_release( p_input );