]> git.sesse.net Git - vlc/blob - modules/misc/audioscrobbler.c
Added psz_trackid, psz_artistid, psz_albumid to vlc_meta_t to store musicbrainz uniqu...
[vlc] / modules / misc / audioscrobbler.c
1 /*****************************************************************************
2  * audioscrobbler.c : audioscrobbler submission plugin
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Rafaël Carré <funman at videolan org>
8  *          Kenneth Ostby <kenneo -at- idi -dot- ntnu -dot- no>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #define _GNU_SOURCE
30 #include <string.h>
31
32 #if defined( WIN32 )
33 #include <time.h>
34 #endif
35 /*
36  * TODO :
37  * implement musicbrainz unique track identifier in p_meta
38  * check meta_engine's state, and remove delaying of metadata reading
39  * check md5 operations on BIGENDIAN and 64 bits architectures
40  */
41 #include <vlc/vlc.h>
42 #include <vlc/intf.h>
43 #include <vlc_meta.h>
44 #include <vlc_md5.h>
45 #include <vlc_block.h>
46 #include <vlc_stream.h>
47 #include <vlc_url.h>
48 #include <network.h>
49 #include <vlc_interaction.h>
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54
55 /* Keeps track of metadata to be submitted, and if song has been submitted */
56 typedef struct audioscrobbler_song_t
57 {
58     char        *psz_a;                /* track artist     */
59     char        *psz_t;                /* track title      */
60     char        *psz_b;                /* track album      */
61     int         i_l;                   /* track length     */
62     char        *psz_m;                /* musicbrainz id   */
63     char        *psz_i;                /* date             */
64     time_t      time_playing;          /* date (epoch)     */
65 } audioscrobbler_song_t;
66
67
68 /* Queue to be submitted to server, 10 songs max */
69 typedef struct audioscrobbler_queue_t
70 {
71     audioscrobbler_song_t   **p_queue;      /* contains up to 10 songs        */
72     int                     i_songs_nb;     /* number of songs                */
73     void                    *p_next_queue;  /* if queue full, pointer to next */
74 } audioscrobbler_queue_t;
75
76 struct intf_sys_t
77 {
78     audioscrobbler_queue_t  *p_first_queue;     /* 1st queue              */
79     vlc_mutex_t             lock;               /* p_sys mutex            */
80
81 /* data about audioscrobbler session */
82     int                     i_interval;         /* last interval recorded */
83     time_t                  time_last_interval; /* when was it recorded ? */
84     char                    *psz_submit_host;   /* where to submit data ? */
85     int                     i_submit_port;      /* at which port ?        */
86     char                    *psz_submit_file;   /* in which file ?        */
87     char                    *psz_username;      /* last.fm username       */
88     vlc_bool_t              b_handshaked;       /* did we handshake ?     */
89     int                     i_post_socket;      /* socket for submission  */
90     char                    *psz_response_md5;  /* md5 response to use    */
91
92 /* data about input elements */
93     audioscrobbler_song_t   *p_current_song;    /* song being played      */
94     time_t                  time_pause;         /* time when vlc paused   */
95     time_t                  time_total_pauses;  /* sum of time in pause   */
96     vlc_bool_t              b_queued;           /* has it been queud ?    */
97     vlc_bool_t              b_metadata_read;    /* did we read metadata ? */
98     vlc_bool_t              b_paused;           /* are we playing ?       */
99     vlc_bool_t              b_waiting_meta;     /* we need fetched data?  */
100 };
101
102 intf_sys_t *p_sys_global;     /* to retrieve p_sys in Run() thread */
103
104 static int  Open    ( vlc_object_t * );
105 static void Close   ( vlc_object_t * );
106 static void Run     ( intf_thread_t * );
107 static int ItemChange   ( vlc_object_t *, const char *,
108                         vlc_value_t, vlc_value_t, void * );
109 static int PlayingChange( vlc_object_t *, const char *,
110                         vlc_value_t, vlc_value_t, void * );
111 static int AddToQueue   ( intf_thread_t *p_this );
112 static int Handshake    ( intf_thread_t *p_sd );
113 static int ReadMetaData ( intf_thread_t *p_this );
114 static int ReadLocalMetaData( intf_thread_t *p_this, input_thread_t  *p_input );
115 void DeleteQueue( audioscrobbler_queue_t *p_queue );
116
117 /*****************************************************************************
118  * Module descriptor
119  ****************************************************************************/
120
121
122 #define APPLICATION_NAME "VLC media player"
123 #define USERNAME_TEXT N_("Username")
124 #define USERNAME_LONGTEXT N_("The username of your last.fm account")
125 #define PASSWORD_TEXT N_("Password")
126 #define PASSWORD_LONGTEXT N_("The password of your last.fm account")
127
128 /* if something goes wrong, we wait at least one minute before trying again */
129 #define DEFAULT_INTERVAL 60
130 /* last.fm client identifier */
131 #define CLIENT_NAME     PACKAGE
132 #define CLIENT_VERSION  VERSION
133
134 /* HTTP POST request : to submit data */
135 #define    POST_REQUEST "POST /%s HTTP/1.1\n"                               \
136                         "Accept-Encoding: identity\n"                       \
137                         "Content-length: %d\n"                              \
138                         "Connection: close\n"                               \
139                         "Content-type: application/x-www-form-urlencoded\n" \
140                         "Host: %s\n"                                        \
141                         "User-agent: VLC Media Player/%s\r\n"               \
142                         "\r\n"                                              \
143                         "%s\r\n"                                            \
144                         "\r\n"
145
146 /* data to submit */
147 #define POST_DATA "u=%s&s=%s&a%%5B%d%%5D=%s&t%%5B%d%%5D=%s" \
148                   "&b%%5B%d%%5D=%s&m%%5B%d%%5D=%s&l%%5B%d%%5D=%d&i%%5B%d%%5D=%s"
149
150 vlc_module_begin();
151     set_category( CAT_INTERFACE );
152     set_subcategory( SUBCAT_INTERFACE_CONTROL );
153     set_shortname( N_( "Audioscrobbler" ) );
154     set_description( N_("Audioscrobbler submission Plugin") );
155     add_string( "lastfm-username", "", NULL,
156                 USERNAME_TEXT, USERNAME_LONGTEXT, VLC_FALSE );
157     add_string( "lastfm-password", "", NULL,
158                 PASSWORD_TEXT, PASSWORD_LONGTEXT, VLC_FALSE );
159     set_capability( "interface", 0 );
160     set_callbacks( Open, Close );
161 vlc_module_end();
162
163 /*****************************************************************************
164  * Open: initialize and create stuff
165  *****************************************************************************/
166
167 static int Open( vlc_object_t *p_this )
168 {
169     playlist_t          *p_playlist;
170
171     intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
172     intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
173     if( !p_sys )
174     {
175       goto error;
176     }
177     vlc_mutex_init( p_this, &p_sys->lock );
178
179     p_sys_global = p_sys;
180     p_sys->psz_submit_host = NULL;
181     p_sys->psz_submit_file = NULL;
182     p_sys->b_handshaked = VLC_FALSE;
183     p_sys->i_interval = 0;
184     p_sys->time_last_interval = time( NULL );
185     p_sys->psz_username = NULL;
186     p_sys->b_paused = VLC_FALSE;
187
188     /* md5 response is 32 chars, + final \0 */
189     p_sys->psz_response_md5 = malloc( sizeof( char ) * 33 );
190     if( !p_sys->psz_response_md5 )
191     {
192         vlc_mutex_destroy ( &p_sys->lock );
193         goto error;
194    }
195
196     p_sys->p_first_queue = malloc( sizeof( audioscrobbler_queue_t ) );
197     if( !p_sys->p_first_queue )
198     {
199         vlc_mutex_destroy( &p_sys->lock );
200         goto error;
201     }
202
203     p_sys->p_current_song = malloc( sizeof( audioscrobbler_song_t ) );
204     if( !p_sys->p_current_song )
205     {
206         vlc_mutex_destroy( &p_sys->lock );
207         goto error;
208     }
209
210     /* queues can't contain more than 10 songs */
211     p_sys->p_first_queue->p_queue =
212         malloc( 10 * sizeof( audioscrobbler_song_t ) );
213     if( !p_sys->p_current_song )
214     {
215         vlc_mutex_destroy( &p_sys->lock );
216         goto error;
217     }
218
219     p_sys->p_first_queue->i_songs_nb = 0;
220     p_sys->p_first_queue->p_next_queue = NULL;
221
222     p_playlist = pl_Yield( p_intf );
223     var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
224     pl_Release( p_playlist );
225
226     p_intf->pf_run = Run;
227
228     return VLC_SUCCESS;
229
230 error:
231     free( p_sys->p_current_song );
232     free( p_sys->p_first_queue );
233     free( p_sys->psz_response_md5 );
234     free( p_sys );
235
236     return VLC_ENOMEM;
237 }
238
239 /*****************************************************************************
240  * Close: destroy interface stuff
241  *****************************************************************************/
242 static void Close( vlc_object_t *p_this )
243 {
244     audioscrobbler_queue_t      *p_current_queue, *p_next_queue;
245     playlist_t                  *p_playlist;
246     input_thread_t              *p_input;
247
248     intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
249     intf_sys_t *p_sys = p_intf->p_sys;
250
251     p_playlist = pl_Yield( p_intf );
252     PL_LOCK;
253     p_input = p_playlist->p_input;
254     var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
255
256     if ( p_input )
257     {
258         vlc_object_yield( p_input );
259         var_DelCallback( p_input, "state", PlayingChange, p_intf );
260         vlc_object_release( p_input );
261     }
262
263     PL_UNLOCK;
264     pl_Release( p_playlist );
265
266     vlc_mutex_lock ( &p_sys->lock );
267     p_current_queue = p_sys->p_first_queue;
268     vlc_mutex_unlock ( &p_sys->lock );
269
270     while( ( p_current_queue->i_songs_nb == 10 ) &&
271         ( p_current_queue->p_next_queue != NULL ) )
272     {
273         p_next_queue = p_current_queue->p_next_queue;
274         DeleteQueue( p_current_queue );
275         free( p_current_queue );
276         p_current_queue = p_next_queue;
277     }
278
279     DeleteQueue( p_current_queue );
280     free( p_current_queue );
281
282     vlc_mutex_lock ( &p_sys->lock );
283     free( p_sys->psz_username );
284     free( p_sys->p_current_song );
285     vlc_mutex_unlock ( &p_sys->lock );
286     vlc_mutex_destroy( &p_sys->lock );
287     free( p_sys );
288 }
289
290 /*****************************************************************************
291  * Run : Handshake with audioscrobbler, then submit songs
292  *****************************************************************************/
293 static void Run( intf_thread_t *p_this )
294 {
295     char                    *psz_submit_string = NULL;
296     int                     i_net_ret;
297     int                     i_song;
298     playlist_t              *p_playlist;
299     uint8_t                 *p_buffer = NULL;
300     char                    *p_buffer_pos = NULL;
301     audioscrobbler_queue_t  *p_first_queue;
302     /* TODO: remove when meta_engine works */
303     time_t                  played_time;
304
305     p_this->p_sys = p_sys_global;
306     intf_sys_t *p_sys = p_this->p_sys;
307
308     /* main loop */
309     while( !p_this->b_die )
310     {
311         /* verify if there is data to submit 
312          * and if waiting interval is finished */
313         if ( ( p_sys->p_first_queue->i_songs_nb > 0 ) &&
314             ( time( NULL ) >=
315             ( p_sys->time_last_interval + p_sys->i_interval )  ) )
316         {
317             /* handshake if needed */
318             if( p_sys->b_handshaked == VLC_FALSE )
319             {
320                 msg_Dbg( p_this, "Handshaking with last.fm ..." );
321  
322                 switch( Handshake( p_this ) )
323                 {
324                     case VLC_ENOMEM:
325                         msg_Err( p_this, "Out of memory" );
326                         return;
327                         break;
328
329                     case VLC_ENOVAR:
330                         /* username not set */
331                         vlc_mutex_unlock ( &p_sys->lock );
332                         intf_UserFatal( p_this, VLC_FALSE,
333                             _("last.fm username not set"),
334                             _("You have to set a username,"
335                             " and then restart VLC.\n"
336                             "Visit https://www.last.fm/join/"
337                             " if you don't have one.")
338                         );
339                         return;
340                         break;
341
342                     case VLC_SUCCESS:
343                         msg_Dbg( p_this, "Handshake successfull :)" );
344                         vlc_mutex_lock ( &p_sys->lock );
345                         p_sys->b_handshaked = VLC_TRUE;
346                         vlc_mutex_unlock ( &p_sys->lock );
347                         break;
348
349                     case VLC_EGENERIC:
350                     default:
351                         /* VLC_EGENERIC : we'll try later */
352                         vlc_mutex_lock ( &p_sys->lock );
353                         p_sys->i_interval = DEFAULT_INTERVAL;
354                         time( &p_sys->time_last_interval );
355                         vlc_mutex_unlock ( &p_sys->lock );
356                         break;
357                 }
358             }
359
360             msg_Dbg( p_this, "Going to submit some data..." );
361             vlc_mutex_lock ( &p_sys->lock );
362             psz_submit_string = malloc( 2048 * sizeof( char ) );
363
364             if (!psz_submit_string)
365             {
366                 msg_Err( p_this, "Out of memory" );
367                 vlc_mutex_unlock ( &p_sys->lock );
368                 return;
369             }
370
371             /* forge the HTTP POST request */
372             for (i_song = 0; i_song < p_sys->p_first_queue->i_songs_nb ;
373                 i_song++ )
374             {
375                 snprintf( psz_submit_string, 2048, POST_DATA,
376                     p_sys->psz_username, p_sys->psz_response_md5,
377                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_a,
378                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_t,
379                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_b,
380                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_m,
381                     i_song, p_sys->p_first_queue->p_queue[i_song]->i_l,
382                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_i
383                 );
384             }
385
386             p_sys->i_post_socket = net_ConnectTCP( p_this,
387                 p_sys->psz_submit_host, p_sys->i_submit_port);
388
389             /* we transmit the data */
390             i_net_ret = net_Printf(
391                 VLC_OBJECT(p_this), p_sys->i_post_socket, NULL,
392                 POST_REQUEST, p_sys->psz_submit_file,
393                 strlen( psz_submit_string), p_sys->psz_submit_file,
394                 VERSION, psz_submit_string
395             );
396
397             if ( i_net_ret == -1 )
398             {
399                 /* If connection fails, we assume we must handshake again */
400                 p_sys->i_interval = DEFAULT_INTERVAL;
401                 time( &p_sys->time_last_interval );
402                 p_sys->b_handshaked = VLC_FALSE;
403                 vlc_mutex_unlock( &p_sys->lock );
404                 continue;
405             }
406
407             p_buffer = ( uint8_t* ) calloc( 1, 1024 );
408             if ( !p_buffer )
409             {
410                 msg_Err( p_this, "Out of memory" );
411                 vlc_mutex_unlock ( &p_sys->lock );
412                 return;
413             }
414
415             i_net_ret = net_Read( p_this, p_sys->i_post_socket, NULL,
416                         p_buffer, 1024, VLC_FALSE );
417             if ( i_net_ret <= 0 )
418             {
419                 /* if we get no answer, something went wrong : try again */
420                 vlc_mutex_unlock( &p_sys->lock );
421                 continue;
422             }
423
424             net_Close( p_sys->i_post_socket );
425
426             /* record interval */
427             p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
428             if ( p_buffer_pos )
429             {
430                 p_sys->i_interval = atoi( p_buffer_pos +
431                                             strlen( "INTERVAL " ) );
432                 time( &p_sys->time_last_interval );
433             }
434
435             p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
436             if ( p_buffer_pos )
437             {
438                 /* woops, something failed */
439                 msg_Dbg( p_this, p_buffer_pos );
440                 vlc_mutex_unlock ( &p_sys->lock );
441                 continue;
442             }
443
444             p_buffer_pos = strstr( ( char * ) p_buffer, "BADAUTH" );
445             if ( p_buffer_pos )
446             {
447                 /* too much time elapsed after last handshake? */
448                 msg_Dbg( p_this, "Authentification failed, handshaking again" );
449                 p_sys->b_handshaked = VLC_FALSE;
450                 vlc_mutex_unlock ( &p_sys->lock );
451                 continue;
452             }
453
454             p_buffer_pos = strstr( ( char * ) p_buffer, "OK" );
455             if ( p_buffer_pos )
456             {
457                 if ( p_sys->p_first_queue->i_songs_nb == 10 )
458                 {
459                     /* if there are more than one queue, delete the 1st */
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;
464                 }
465                 else
466                 {
467                     DeleteQueue( p_sys->p_first_queue );
468                     p_sys->p_first_queue->i_songs_nb = 0;
469                 }
470                 msg_Dbg( p_this, "Submission successfull!" );
471             }
472             vlc_mutex_unlock ( &p_sys->lock );
473         } /* data transmission finished or skipped */
474
475         msleep( INTF_IDLE_SLEEP );
476
477         p_playlist = pl_Yield( p_this );
478         PL_LOCK;
479         if( p_playlist->request.i_status == PLAYLIST_STOPPED )
480         {
481             PL_UNLOCK;
482             pl_Release( p_playlist );
483             /* if we stopped, we won't submit playing song */
484             vlc_mutex_lock( &p_sys->lock );
485             p_sys->b_queued = VLC_TRUE;
486             p_sys->b_metadata_read = VLC_TRUE;
487             vlc_mutex_unlock( &p_sys->lock );
488         }
489         else
490         {
491             PL_UNLOCK;
492             pl_Release( p_playlist );
493         }
494
495         vlc_mutex_lock( &p_sys->lock );
496         if( p_sys->b_metadata_read == VLC_FALSE )
497         {
498             /* we read the metadata of the playing song */
499             /* TODO: remove when meta_engine works */
500             time( &played_time );
501             played_time -= p_sys->p_current_song->time_playing;
502             played_time -= p_sys->time_total_pauses;
503
504             vlc_mutex_unlock( &p_sys->lock );
505
506             /* TODO: remove when meta_engine works */
507             if( played_time > 10 )
508             {
509                 if ( ReadMetaData( p_this ) == VLC_ENOMEM )
510                 {
511                     msg_Err( p_this, "Out of memory" );
512                     return;
513                 }
514             }
515         }
516         else
517         {
518             /* we add the playing song into the queue */
519             if( ( p_sys->b_queued == VLC_FALSE )
520                 && ( p_sys->b_paused == VLC_FALSE ) )
521             {
522                 vlc_mutex_unlock( &p_sys->lock );
523                 if( AddToQueue( p_this ) == VLC_ENOMEM )
524                 {
525                     msg_Err( p_this, "Out of memory" );
526                     return;
527                 }
528             }
529             else
530             {
531                 vlc_mutex_unlock( &p_sys->lock );
532             }
533         }
534     }
535 }
536
537 /*****************************************************************************
538  * PlayingChange: Playing status change callback
539  *****************************************************************************/
540 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
541                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
542 {
543     intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
544     intf_sys_t *p_sys = p_intf->p_sys;
545
546     (void)p_this; (void)psz_var; (void)oldval;
547
548     vlc_mutex_lock( &p_sys->lock );
549
550     if( newval.i_int == PAUSE_S )
551     {
552         time( &p_sys->time_pause );
553         p_sys->b_paused = VLC_TRUE;
554     }
555
556     if( newval.i_int == PLAYING_S )
557     {
558         p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
559         p_sys->b_paused = VLC_FALSE;
560     }
561
562     vlc_mutex_unlock( &p_sys->lock );
563
564     return VLC_SUCCESS;
565 }
566
567 /*****************************************************************************
568  * ItemChange: Playlist item change callback
569  *****************************************************************************/
570 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
571                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
572 {
573     playlist_t          *p_playlist;
574     input_thread_t      *p_input = NULL;
575     time_t              epoch;
576     struct tm           *epoch_tm;
577     char                psz_date[20];
578
579     (void)p_this; (void)psz_var; (void)oldval; (void)newval;
580
581     intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
582     intf_sys_t *p_sys = p_intf->p_sys;
583
584     p_playlist = pl_Yield( p_intf );
585     PL_LOCK;
586     p_input = p_playlist->p_input;
587
588     if( !p_input )
589     {
590         PL_UNLOCK;
591         pl_Release( p_playlist );
592         vlc_mutex_lock( &p_sys->lock );
593
594         /* we won't read p_input */
595         p_sys->b_queued = VLC_TRUE;
596         p_sys->b_metadata_read = VLC_TRUE;
597
598         vlc_mutex_unlock( &p_sys->lock );
599         return VLC_SUCCESS;
600     }
601
602     vlc_object_yield( p_input );
603     PL_UNLOCK;
604     pl_Release( p_playlist );
605
606     var_AddCallback( p_input, "state", PlayingChange, p_intf );
607
608     vlc_mutex_lock ( &p_sys->lock );
609
610     /* reset pause counter */
611     p_sys->time_total_pauses = 0;
612
613     /* we'll read metadata when it's present */
614     p_sys->b_metadata_read = VLC_FALSE;
615     p_sys->b_waiting_meta = VLC_FALSE;
616
617     time( &epoch );
618     epoch_tm = gmtime( &epoch );
619     snprintf( psz_date, 20, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
620         epoch_tm->tm_year+1900, epoch_tm->tm_mon+1, epoch_tm->tm_mday,
621         epoch_tm->tm_hour, epoch_tm->tm_min, epoch_tm->tm_sec );
622
623     p_sys->p_current_song->psz_i = encode_URI_component( psz_date );
624     p_sys->p_current_song->time_playing = epoch;
625
626     p_sys->b_paused = ( p_input->b_dead || !p_input->input.p_item->psz_name )
627                       ? VLC_TRUE : VLC_FALSE;
628
629     vlc_mutex_unlock( &p_sys->lock );
630
631     vlc_object_release( p_input );
632     return VLC_SUCCESS;
633 }
634
635 /*****************************************************************************
636  * AddToQueue: Add the played song to the queue to be submitted
637  *****************************************************************************/
638 static int AddToQueue ( intf_thread_t *p_this )
639 {
640     int                         i_songs_nb;
641     time_t                      played_time;
642     audioscrobbler_queue_t      *p_queue = NULL, *p_next_queue = NULL;
643
644     intf_sys_t *p_sys = p_this->p_sys;
645
646     /* wait for the user to listen enough before submitting */
647     time ( &played_time );
648     played_time -= p_sys->p_current_song->time_playing;
649     played_time -= p_sys->time_total_pauses;
650
651     vlc_mutex_lock( &p_sys->lock );
652     if( ( played_time < 240 )
653         && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
654     {
655         vlc_mutex_unlock ( &p_sys->lock );
656         return VLC_SUCCESS;
657     }
658
659     if( p_sys->p_current_song->i_l < 30 )
660     {
661         msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
662         p_sys->b_queued = VLC_TRUE;
663         vlc_mutex_unlock ( &p_sys->lock );
664         return VLC_SUCCESS;
665     }
666
667     if( !*p_sys->p_current_song->psz_a || !*p_sys->p_current_song->psz_t )
668     {
669         msg_Dbg( p_this, "Missing artist or title -> not submitting" );
670         p_sys->b_queued = VLC_TRUE;
671         vlc_mutex_unlock ( &p_sys->lock );
672         return VLC_SUCCESS;
673     }
674
675     msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
676
677     /* go to last queue */
678     p_queue = p_sys->p_first_queue;
679     while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
680     {
681         p_queue = p_queue->p_next_queue;
682     }
683
684     i_songs_nb = p_queue->i_songs_nb;
685
686     if( i_songs_nb == 10 )
687     {
688         p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
689         if( !p_next_queue )
690         {
691             vlc_mutex_unlock ( &p_sys->lock );
692             return VLC_ENOMEM;
693         }
694         p_queue->p_next_queue = p_next_queue;
695         i_songs_nb = 0;
696         p_queue = p_next_queue;
697         p_queue->i_songs_nb = i_songs_nb;
698     }
699
700     p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
701
702     p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
703
704     p_queue->p_queue[i_songs_nb]->psz_a =
705         strdup( p_sys->p_current_song->psz_a );
706
707     p_queue->p_queue[i_songs_nb]->psz_t =
708         strdup( p_sys->p_current_song->psz_t );
709
710     p_queue->p_queue[i_songs_nb]->psz_b =
711         strdup( p_sys->p_current_song->psz_b );
712
713     p_queue->p_queue[i_songs_nb]->psz_m =
714         strdup( p_sys->p_current_song->psz_m );
715
716     p_queue->p_queue[i_songs_nb]->psz_i =
717         strdup( p_sys->p_current_song->psz_i );
718
719     p_queue->i_songs_nb++;
720     p_sys->b_queued = VLC_TRUE;
721
722     vlc_mutex_unlock( &p_sys->lock );
723
724     return VLC_SUCCESS;
725 }
726
727 /*****************************************************************************
728  * Handshake : Init audioscrobbler connection
729  *****************************************************************************/
730 static int Handshake( intf_thread_t *p_this )
731 {
732     char                *psz_password = NULL;
733     struct md5_s        *p_struct_md5 = NULL;
734     char                *psz_password_md5 = NULL;
735     char                *ps_challenge_md5 = NULL;
736
737     stream_t            *p_stream;
738     char                *psz_handshake_url = NULL;
739
740     uint8_t             *p_buffer = NULL;
741     char                *p_buffer_pos = NULL;
742     char                *psz_buffer_substring = NULL;
743     char                *psz_url_parser = NULL;
744     int                 i_url_pos, i;
745
746     intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
747     intf_sys_t *p_sys = p_this->p_sys;
748
749     vlc_mutex_lock ( &p_sys->lock );
750
751     p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
752     if ( !p_sys->psz_username )
753     {
754         goto memerror;
755     }
756
757     if ( !*p_sys->psz_username )
758     {
759         return VLC_ENOVAR;
760     }
761
762     psz_handshake_url = malloc( 1024 );
763     if ( !psz_handshake_url )
764     {
765         goto memerror;
766     }
767
768     snprintf( psz_handshake_url, 1024,
769         "http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s",
770         CLIENT_NAME, CLIENT_VERSION, p_sys->psz_username );
771
772     /* send the http handshake request */
773     p_stream = stream_UrlNew( p_intf, psz_handshake_url);
774
775     free( psz_handshake_url );
776
777     if( !p_stream )
778     {
779         vlc_mutex_unlock ( &p_sys->lock );
780         return VLC_EGENERIC;
781     }
782
783     p_buffer = ( uint8_t* ) calloc( 1, 1024 );
784     if ( !p_buffer )
785     {
786         stream_Delete( p_stream );
787         goto memerror;
788     }
789
790     /* read answer */
791     if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
792     {
793         stream_Delete( p_stream );
794         goto generic_error;
795     }
796
797     stream_Delete( p_stream );
798
799     /* record interval before next submission */
800     p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
801     if ( p_buffer_pos )
802     {
803         p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
804         time( &p_sys->time_last_interval );
805     }
806
807     p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
808     if ( p_buffer_pos )
809     {
810         /* handshake request failed */
811         msg_Dbg( p_this, p_buffer_pos );
812         goto generic_error;
813     }
814
815     p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
816     if ( p_buffer_pos )
817     {
818         /* username does not exist */
819         intf_UserFatal( p_this, VLC_FALSE, _("Bad last.fm Username"),
820             _("last.fm username is incorrect, please verify your settings")
821         );
822         goto generic_error;
823     }
824
825     p_buffer_pos = strstr( ( char * ) p_buffer, "UPDATE" );
826     if ( p_buffer_pos )
827     {
828         /* protocol has been updated, developers need to work :) */
829         msg_Dbg( p_intf, "Protocol updated" );
830         msg_Dbg( p_intf, p_buffer_pos );
831     }
832
833     else
834     {
835         p_buffer_pos = strstr( ( char * ) p_buffer, "UPTODATE" );
836         if ( !p_buffer_pos )
837         {
838             msg_Dbg( p_intf, "Protocol error" );
839             goto generic_error;
840         }
841     }
842
843     psz_buffer_substring = strndup( strstr( p_buffer_pos, "\n" ) + 1, 32 );
844     if ( !psz_buffer_substring )
845     {
846         goto memerror;
847     }
848     else
849     {
850         ps_challenge_md5 = malloc( sizeof( char ) * 32 );
851         if ( !ps_challenge_md5 )
852         {
853             goto memerror;
854         }
855         memcpy( ps_challenge_md5, psz_buffer_substring, 32 );
856         free( psz_buffer_substring );
857     }
858
859     p_buffer_pos = ( void* ) strstr( ( char* ) p_buffer, "http://" );
860
861     free( p_sys->psz_submit_host );
862     free( p_sys->psz_submit_file );
863
864     psz_url_parser = p_buffer_pos + strlen( "http://" );
865
866     i_url_pos = strcspn( psz_url_parser, ":" );
867     p_sys->psz_submit_host = strndup( psz_url_parser, i_url_pos );
868
869     p_sys->i_submit_port = atoi( psz_url_parser + i_url_pos + 1 );
870
871     psz_url_parser += strcspn( psz_url_parser , "/" ) + 1;
872     i_url_pos = strcspn( psz_url_parser, "\n" );
873     p_sys->psz_submit_file = strndup( psz_url_parser, i_url_pos );
874
875     free(p_buffer);
876
877     p_struct_md5 = malloc( sizeof( struct md5_s ) );
878     if( !p_struct_md5 )
879     {
880         goto memerror;
881     }
882
883     psz_password = config_GetPsz(p_this, "lastfm-password");
884     if ( !psz_password )
885     {
886          goto memerror;
887     }
888
889     /* generates a md5 hash of the password */
890     InitMD5( p_struct_md5 );
891     AddMD5( p_struct_md5, ( uint8_t* ) psz_password, strlen( psz_password ) );
892     EndMD5( p_struct_md5 );
893
894     free( psz_password );
895
896     psz_password_md5 = malloc ( 33 * sizeof( char ) );
897     if ( !psz_password_md5 )
898     {
899         goto memerror;
900     }
901
902     for ( i = 0; i < 4; i++ )
903     {
904         sprintf( &psz_password_md5[8*i], "%02x%02x%02x%02x",
905             p_struct_md5->p_digest[i] & 0xff,
906             ( p_struct_md5->p_digest[i] >> 8 ) & 0xff,
907             ( p_struct_md5->p_digest[i] >> 16 ) & 0xff,
908             p_struct_md5->p_digest[i] >> 24
909         );
910     }
911
912     /* generates a md5 hash of :
913      * - md5 hash of the password
914      * - md5 challenge sent by last.fm server
915      */
916     InitMD5( p_struct_md5 );
917     AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
918     AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
919     EndMD5( p_struct_md5 );
920
921     free( ps_challenge_md5 );
922     free( psz_password_md5 );
923
924     for ( i = 0; i < 4; i++ )
925     {
926         sprintf( &p_sys->psz_response_md5[8*i], "%02x%02x%02x%02x",
927             p_struct_md5->p_digest[i] & 0xff,
928             ( p_struct_md5->p_digest[i] >> 8 ) & 0xff,
929             ( p_struct_md5->p_digest[i] >> 16 ) & 0xff,
930             p_struct_md5->p_digest[i] >> 24
931         );
932     }
933
934     p_sys->psz_response_md5[32] = 0;
935
936     vlc_mutex_unlock ( &p_sys->lock );
937
938     return VLC_SUCCESS;
939
940 generic_error:
941     free( p_buffer );
942     vlc_mutex_unlock( &p_sys->lock );
943     return VLC_EGENERIC;
944
945 memerror:
946     free( p_buffer );
947     free( p_struct_md5 );
948     free( psz_buffer_substring );
949
950     vlc_mutex_unlock( &p_sys->lock );
951     return VLC_ENOMEM;
952 }
953
954 /*****************************************************************************
955  * DeleteQueue : Free all songs from an audioscrobbler_queue_t
956  *****************************************************************************/
957 void DeleteQueue( audioscrobbler_queue_t *p_queue )
958 {
959     int     i;
960
961     for( i = 0; i < p_queue->i_songs_nb; i++ )
962     {
963         free( p_queue->p_queue[i]->psz_a );
964         free( p_queue->p_queue[i]->psz_b );
965         free( p_queue->p_queue[i]->psz_t );
966         free( p_queue->p_queue[i]->psz_i );
967         free( p_queue->p_queue[i] );
968     }
969 }
970
971 /*****************************************************************************
972  * ReadMetaData : Read meta data when parsed by vlc
973  * or wait for fetching if unavailable
974  *****************************************************************************/
975 static int ReadMetaData( intf_thread_t *p_this )
976 {
977     playlist_t          *p_playlist;
978     input_thread_t      *p_input = NULL;
979     vlc_value_t         video_val;
980
981     intf_sys_t *p_sys = p_this->p_sys;
982
983     p_playlist = pl_Yield( p_this );
984     PL_LOCK;
985     p_input = p_playlist->p_input;
986
987     if( !p_input )
988     {
989         PL_UNLOCK;
990         pl_Release( p_playlist );
991         return( VLC_SUCCESS );
992     }
993
994     vlc_object_yield( p_input );
995     PL_UNLOCK;
996     pl_Release( p_playlist );
997
998     var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
999     if( ( video_val.i_int > 0 ) || \
1000         ( p_input->input.p_item->i_type == ITEM_TYPE_NET ) )
1001     {
1002         msg_Dbg( p_this, "Not an audio file -> no submission");
1003         vlc_object_release( p_input );
1004
1005         vlc_mutex_lock( &p_sys->lock );
1006         p_sys->b_queued = VLC_TRUE;
1007         p_sys->b_metadata_read = VLC_TRUE;
1008         vlc_mutex_unlock( &p_sys->lock );
1009
1010         return VLC_SUCCESS;
1011     }
1012
1013     return ReadLocalMetaData( p_this, p_input );    
1014 }
1015
1016 /*****************************************************************************
1017  * ReadLocalMetaData : Puts current song's meta data in p_sys->p_current_song
1018  *****************************************************************************/
1019 static int ReadLocalMetaData( intf_thread_t *p_this, input_thread_t  *p_input )
1020 {
1021     char                *psz_title = NULL;
1022     char                *psz_artist = NULL;
1023     char                *psz_album = NULL;
1024     char                *psz_trackid = NULL;
1025     int                 i_length = -1;
1026     vlc_bool_t          b_waiting;
1027     int                 i_status;
1028
1029     intf_sys_t *p_sys = p_this->p_sys;
1030
1031     i_status = p_input->input.p_item->p_meta->i_status;
1032
1033     vlc_mutex_lock( &p_sys->lock );
1034     b_waiting = p_sys->b_waiting_meta;
1035     vlc_mutex_unlock( &p_sys->lock );
1036
1037     /* TODO : remove if (1) when meta_engine works */
1038     if ( (1/*( i_status & ITEM_PREPARSED )*/&& ( b_waiting == VLC_FALSE ) ) || \
1039         ( ( i_status & ITEM_META_FETCHED ) && ( b_waiting == VLC_TRUE ) ) )
1040     {
1041         if ( p_input->input.p_item->p_meta->psz_artist )
1042         {
1043             psz_artist = encode_URI_component(
1044                 p_input->input.p_item->p_meta->psz_artist );
1045             if ( !psz_artist )
1046             {
1047                 goto error;
1048             }
1049         }
1050         else
1051         {
1052             msg_Dbg( p_this, "No artist.." );
1053             if ( b_waiting == VLC_TRUE )
1054             {
1055                 psz_artist = calloc( 1, sizeof( char ) );
1056             }
1057             else
1058             {
1059                 goto waiting_meta_data_fetching;
1060             }
1061         }
1062         if ( p_input->input.p_item->psz_name )
1063         {
1064             psz_title = encode_URI_component( p_input->input.p_item->psz_name );
1065             if ( !psz_title )
1066             {
1067                 goto error;
1068             }
1069         }
1070         else
1071         {
1072             msg_Dbg( p_this, "No track name.." );
1073             if ( b_waiting == VLC_TRUE )
1074             {
1075                 psz_title = calloc( 1, sizeof( char ) );
1076             }
1077             else
1078             {
1079                 goto waiting_meta_data_fetching;
1080             }
1081         }
1082
1083         if ( p_input->input.p_item->p_meta->psz_trackid )
1084         {
1085             psz_trackid = strdup( p_input->input.p_item->p_meta->psz_trackid );
1086             if ( !psz_trackid )
1087             {
1088                 goto error;
1089             }
1090         }
1091         else
1092         {
1093             psz_trackid = calloc( 1, sizeof( char ) );
1094         }
1095
1096         if ( p_input->input.p_item->p_meta->psz_album )
1097         {
1098             psz_album = encode_URI_component(
1099                 p_input->input.p_item->p_meta->psz_album );
1100             if ( !psz_album )
1101             {
1102                 goto error;
1103             }
1104         }
1105         else
1106         {
1107             psz_album = calloc( 1, sizeof( char ) );
1108         }
1109
1110         i_length = p_input->input.p_item->i_duration / 1000000;
1111
1112         vlc_object_release( p_input );
1113
1114         vlc_mutex_lock ( &p_sys->lock );
1115
1116         p_sys->p_current_song->psz_a = strdup( psz_artist );
1117         p_sys->p_current_song->psz_t = strdup( psz_title );
1118         p_sys->p_current_song->psz_b = strdup( psz_album );
1119         p_sys->p_current_song->psz_m = strdup( psz_trackid );
1120         p_sys->p_current_song->i_l = i_length;
1121         p_sys->b_queued = VLC_FALSE;
1122         p_sys->b_metadata_read = VLC_TRUE;
1123
1124         vlc_mutex_unlock ( &p_sys->lock );
1125
1126         msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1127
1128         free( psz_title );
1129         free( psz_artist );
1130         free( psz_album );
1131         free( psz_trackid );
1132
1133         return VLC_SUCCESS;
1134     }
1135     
1136     return VLC_SUCCESS;
1137
1138 waiting_meta_data_fetching:
1139     vlc_object_release( p_input );
1140
1141     vlc_mutex_lock( &p_sys->lock );
1142     p_sys->b_waiting_meta = VLC_TRUE;
1143     vlc_mutex_unlock( &p_sys->lock );
1144
1145     free( psz_artist );
1146     free( psz_album );
1147     free( psz_title );
1148
1149     return VLC_SUCCESS;
1150
1151 error:
1152     vlc_object_release( p_input );
1153
1154     free( psz_artist );
1155     free( psz_album );
1156     free( psz_title );
1157     free( psz_trackid );
1158     return VLC_ENOMEM;
1159 }