1 /*****************************************************************************
2 * daap.c : Apple DAAP discovery module
3 *****************************************************************************
4 * Copyright (C) 2004 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
34 #include <vlc/input.h>
36 #include <daap/client.h>
38 /************************************************************************
39 * Macros and definitions
40 ************************************************************************/
42 /*****************************************************************************
44 *****************************************************************************/
47 static int Open ( vlc_object_t * );
48 static void Close( vlc_object_t * );
49 static int OpenAccess ( vlc_object_t * );
50 static void CloseAccess( vlc_object_t * );
53 set_description( _("DAAP shares") );
54 set_category( CAT_PLAYLIST );
55 set_subcategory( SUBCAT_PLAYLIST_SD );
57 set_capability( "services_discovery", 0 );
58 set_callbacks( Open, Close );
61 set_description( _( "DAAP access") );
62 set_capability( "access2", 0 );
63 set_callbacks( OpenAccess, CloseAccess );
67 /*****************************************************************************
69 *****************************************************************************/
71 typedef struct dhost_s {
75 DAAP_SClientHost *p_host;
80 playlist_item_t *p_node;
82 DAAP_ClientHost_DatabaseItem *p_songs;
86 typedef struct daap_db_s {
92 vlc_mutex_t search_lock;
95 struct services_discovery_sys_t {
96 playlist_item_t *p_node;
98 DAAP_SClient *p_client;
99 DAAP_SClientHost *p_host;
104 struct access_sys_t {
113 DAAP_ClientHost_Song song;
114 DAAP_ClientHost_DatabaseItem songdata;
119 /*****************************************************************************
121 *****************************************************************************/
124 static void Run ( services_discovery_t *p_sd );
125 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
126 int i_pos, void *p_context );
127 static int EnumerateCallback( DAAP_SClient *p_client,
128 DAAP_SClientHost *p_host,
130 static void OnHostsUpdate( services_discovery_t *p_sd );
131 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host );
132 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host );
134 static int Control( access_t *p_access, int i_query, va_list args );
135 static int Read( access_t *, uint8_t *, int );
136 static int Seek( access_t *, int64_t );
138 /*****************************************************************************
139 * Open: initialize and create stuff
140 *****************************************************************************/
141 static int Open( vlc_object_t *p_this )
143 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
144 services_discovery_sys_t *p_sys = (services_discovery_sys_t *)
145 malloc( sizeof( services_discovery_sys_t ) );
147 playlist_t *p_playlist;
148 playlist_view_t *p_view;
154 p_sys->p_db = (daap_db_t *)malloc( sizeof( daap_db_t ) );
159 p_sys->p_db->pp_hosts = NULL;
160 p_sys->p_db->i_hosts = 0;
162 var_Create( p_sd->p_vlc, "daap-db", VLC_VAR_ADDRESS );
163 val.p_address = p_sys->p_db;
164 var_Set( p_sd->p_vlc, "daap-db", val );
166 vlc_mutex_init( p_sd, &p_sys->p_db->search_lock );
169 p_sys->p_client = DAAP_Client_Create( Callback, p_sd );
170 p_sys->p_db->i_last_id = 0;
172 /* TODO: Set debugging correctly */
173 // DAAP_Client_SetDebug( p_sys->p_client, "+trace" );
176 /* Create our playlist node */
177 p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
181 msg_Warn( p_sd, "unable to find playlist, cancelling DAAP" );
185 p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
186 p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
187 _("DAAP shares"), p_view->p_root );
188 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
190 val.b_bool = VLC_TRUE;
191 var_Set( p_playlist, "intf-change", val );
192 vlc_object_release( p_playlist );
197 static int OpenAccess( vlc_object_t *p_this )
199 access_t *p_access = (access_t*)p_this;
202 vlc_bool_t b_found = VLC_FALSE;
205 p_access->pf_read = Read;
206 p_access->pf_block = NULL;
207 p_access->pf_control = Control;
208 p_access->pf_seek = Seek;
209 p_access->info.i_update = 0;
210 p_access->info.i_size = 0;
211 p_access->info.i_pos = 0;
212 p_access->info.b_eof = VLC_FALSE;
213 p_access->info.i_title = 0;
214 p_access->info.i_seekpoint = 0;
215 p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
216 memset( p_sys, 0, sizeof( access_sys_t ) );
218 i_ret = var_Get( p_access->p_vlc , "daap-db", &val );
219 p_sys->p_db = val.p_address;
221 if( p_sys->p_db == NULL || i_ret )
223 msg_Err( p_access, "The DAAP services_discovery module must be enabled" );
227 vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
229 p_sys->p_host = NULL;
230 p_sys->i_host = atoi( p_sys->url.psz_host ) ;
231 p_sys->i_song = p_sys->url.i_port;
233 if( !p_sys->i_host || !p_sys->i_song )
235 msg_Err( p_access, "invalid host or song" );
239 /* Search the host */
240 vlc_mutex_lock( &p_sys->p_db->search_lock );
241 for( i = 0 ; i < p_sys->p_db->i_hosts ; i++ )
243 if( p_sys->p_db->pp_hosts[i]->i_id == p_sys->i_host )
245 p_sys->p_host = p_sys->p_db->pp_hosts[i];
251 for( i = 0 ; i < p_sys->p_host->i_songs ; i++ )
253 if( p_sys->p_host->p_songs[i].id == p_sys->i_song )
255 p_sys->songdata = p_sys->p_host->p_songs[i];
262 msg_Err( p_access, "invalid song (not found in %i)",
263 p_sys->p_host->i_songs );
268 msg_Warn( p_access, "invalid host (not found in %i)",
269 p_sys->p_db->i_hosts );
271 vlc_mutex_unlock( &p_sys->p_db->search_lock );
273 if( !p_sys->p_host || !b_found )
279 msg_Dbg( p_access, "Downloading %s song %i (db %i)",
280 p_sys->songdata.songformat,
281 p_sys->i_song, p_sys->p_host->i_database_id );
283 /* FIXME: wait for better method by upstream */
284 i_ret = DAAP_ClientHost_GetAudioFile( p_sys->p_host->p_host,
285 p_sys->p_host->i_database_id,
287 p_sys->songdata.songformat,
290 msg_Dbg( p_access, "Finished downloading, read %i bytes (ret %i)",
291 p_sys->song.size, i_ret );
293 p_access->info.i_size = p_sys->song.size;
301 /*****************************************************************************
303 *****************************************************************************/
304 static void Close( vlc_object_t *p_this )
306 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
307 services_discovery_sys_t *p_sys = p_sd->p_sys;
309 playlist_t *p_playlist;
312 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
315 for( i = 0 ; i< p_sys->p_db->i_hosts ; i++ )
317 FreeHost( p_sd, p_sys->p_db->pp_hosts[i] );
320 var_Destroy( p_sd->p_vlc, "daap-db" );
324 playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
325 vlc_object_release( p_playlist );
331 static void CloseAccess( vlc_object_t *p_this )
333 access_t *p_access = (access_t*) p_this;
334 access_sys_t *p_sys = p_access->p_sys;
340 p_sys->song.data = p_sys->p_orig_buffer;
341 p_sys->song.size = p_sys->i_orig_size;
342 DAAP_ClientHost_FreeAudioFile( p_sys->p_host->p_host, &p_sys->song );
348 /*****************************************************************************
349 * Run: main DAAP thread
350 *****************************************************************************/
351 static void Run( services_discovery_t *p_sd )
353 while( !p_sd->b_die )
359 /*****************************************************************************
361 *****************************************************************************/
362 static int Control( access_t *p_access, int i_query, va_list args )
368 case ACCESS_CAN_SEEK:
369 case ACCESS_CAN_FASTSEEK:
370 pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
373 case ACCESS_CAN_PAUSE:
374 case ACCESS_CAN_CONTROL_PACE:
375 pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
379 case ACCESS_GET_PTS_DELAY:
380 pi_64 = (int64_t *)va_arg( args, int64_t *);
381 *pi_64 = (int64_t)300000;
384 case ACCESS_SET_PAUSE_STATE:
387 case ACCESS_GET_TITLE_INFO:
388 case ACCESS_SET_TITLE:
389 case ACCESS_SET_SEEKPOINT:
390 case ACCESS_SET_PRIVATE_ID_STATE:
394 msg_Warn( p_access, "unimplemented query control %i", i_query );
400 static int Read( access_t *p_access, uint8_t *p_buffer, int i_size )
402 access_sys_t *p_sys = (access_sys_t *)p_access->p_sys;
405 if( i_size < p_sys->song.size && p_sys->song.size > 0 )
409 else if( p_sys->song.size == 0 )
415 i_send = p_sys->song.size;
418 memcpy( p_buffer, p_sys->song.data, i_send );
419 p_sys->song.size -= i_send;
420 p_sys->song.data += i_send;
425 static int Seek( access_t *p_access, int64_t i_pos )
427 if( i_pos > p_access->p_sys->i_orig_size )
431 p_access->p_sys->song.size = p_access->p_sys->i_orig_size - i_pos;
432 p_access->p_sys->song.data = p_access->p_sys->p_orig_buffer + i_pos;
436 /**************************************************************
438 **************************************************************/
439 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
440 int i_pos, void *p_context )
442 services_discovery_t *p_sd = (services_discovery_t *)p_context;
444 if( status == DAAP_STATUS_hostschanged )
446 OnHostsUpdate( p_sd );
448 else if( status == DAAP_STATUS_downloading )
453 static void OnHostsUpdate( services_discovery_t *p_sd )
457 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
459 p_sd->p_sys->p_db->pp_hosts[i]->b_updated = VLC_FALSE;
460 p_sd->p_sys->p_db->pp_hosts[i]->b_new = VLC_FALSE;
463 vlc_mutex_lock( &p_sd->p_sys->p_db->search_lock );
464 DAAP_Client_EnumerateHosts( p_sd->p_sys->p_client, EnumerateCallback, p_sd);
466 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
468 if( p_sd->p_sys->p_db->pp_hosts[i]->b_updated == VLC_FALSE )
470 dhost_t *p_host = p_sd->p_sys->p_db->pp_hosts[i];
471 FreeHost( p_sd, p_host );
472 REMOVE_ELEM( p_sd->p_sys->p_db->pp_hosts,
473 p_sd->p_sys->p_db->i_hosts, i );
476 vlc_mutex_unlock( &p_sd->p_sys->p_db->search_lock );
478 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
480 if( p_sd->p_sys->p_db->pp_hosts[i]->b_new )
481 ProcessHost( p_sd, p_sd->p_sys->p_db->pp_hosts[i] );
485 static int EnumerateCallback( DAAP_SClient *p_client,
486 DAAP_SClientHost *p_host,
490 int i_size = DAAP_ClientHost_GetSharename( p_host, NULL, 0 );
491 vlc_bool_t b_found = VLC_FALSE;
492 char *psz_buffer = (char *)malloc( i_size );
493 DAAP_ClientHost_GetSharename( p_host, psz_buffer, i_size );
495 services_discovery_t *p_sd = (services_discovery_t *)p_context;
496 services_discovery_sys_t *p_sys = p_sd->p_sys;
498 for( i = 0 ; i< p_sys->p_db->i_hosts; i++ )
500 if( !strcmp( p_sys->p_db->pp_hosts[i]->psz_name, psz_buffer ) )
502 p_sys->p_db->pp_hosts[i]->b_updated = VLC_TRUE;
510 dhost_t *p_vlchost = (dhost_t *)malloc( sizeof( dhost_t ) );
511 p_vlchost->p_node = NULL;
512 p_vlchost->p_host = p_host;
513 p_vlchost->psz_name = psz_buffer;
514 p_vlchost->b_new = VLC_TRUE;
515 p_vlchost->b_updated = VLC_TRUE;
516 INSERT_ELEM( p_sys->p_db->pp_hosts, p_sys->p_db->i_hosts,
517 p_sys->p_db->i_hosts, p_vlchost );
523 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host )
525 int i_dbsize, i_db, i, i_songsize, i_ret;
526 int i_size = DAAP_ClientHost_GetSharename( p_host->p_host, NULL, 0 );
528 playlist_t *p_playlist;
530 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
538 /* Connect to host */
541 p_host->psz_name = (char *)malloc( i_size );
542 p_host->b_new = VLC_FALSE;
543 DAAP_ClientHost_GetSharename( p_host->p_host, p_host->psz_name ,
546 msg_Dbg( p_sd, "new share %s", p_host->psz_name );
547 DAAP_ClientHost_AddRef( p_host->p_host );
548 i_ret = DAAP_ClientHost_Connect( p_host->p_host );
551 msg_Warn( p_sd, "unable to connect to DAAP host %s",
553 // DAAP_ClientHost_Release( p_host->p_host );
554 vlc_object_release( p_playlist );
558 p_host->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
560 p_sd->p_sys->p_node );
561 p_host->i_id = ++p_sd->p_sys->p_db->i_last_id;
565 i_dbsize = DAAP_ClientHost_GetDatabases( p_host->p_host, NULL, NULL, 0 );
567 DAAP_ClientHost_Database *p_database = malloc( i_dbsize );
568 DAAP_ClientHost_GetDatabases( p_host->p_host, p_database, &i_db, i_dbsize );
571 if( !i_db || !p_database )
573 msg_Warn( p_sd, "no database on DAAP host %s", p_host->psz_name );
574 vlc_object_release( p_playlist );
578 /* We only use the first database */
579 p_host->i_database_id = p_database[0].id;
582 i_songsize = DAAP_ClientHost_GetDatabaseItems( p_host->p_host,
583 p_host->i_database_id,
587 vlc_object_release( p_playlist );
590 p_host->p_songs = malloc( i_songsize );
592 DAAP_ClientHost_GetDatabaseItems( p_host->p_host ,
593 p_host->i_database_id,
595 &p_host->i_songs, i_songsize );
597 for( i = 0; i< p_host->i_songs; i++ )
599 playlist_item_t *p_item;
600 int i_len = 7 + 10 + 1 + 10 ; /* "daap://" + host + ":" + song */
601 char *psz_buff = (char *)malloc( i_len );
603 snprintf( psz_buff, i_len, "daap://%i:%i", p_host->i_id,
604 p_host->p_songs[i].id );
605 p_item = playlist_ItemNew( p_sd, psz_buff,
606 p_host->p_songs[i].itemname );
607 vlc_input_item_AddInfo( &p_item->input, _(VLC_META_INFO_CAT),
608 _(VLC_META_ARTIST), p_host->p_songs[i].songartist );
609 vlc_input_item_AddInfo( &p_item->input, _(VLC_META_INFO_CAT),
610 _(VLC_META_COLLECTION), p_host->p_songs[i].songalbum );
612 playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY,
613 p_host->p_node, PLAYLIST_APPEND, PLAYLIST_END );
617 DAAP_ClientHost_AsyncWaitUpdate( p_host->p_host );
619 vlc_object_release( p_playlist );
622 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host )
624 playlist_t *p_playlist;
628 DAAP_ClientHost_Disconnect( p_host->p_host );
629 DAAP_ClientHost_Release( p_host->p_host );
632 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
637 playlist_NodeDelete( p_playlist, p_host->p_node, VLC_TRUE ,
639 vlc_object_release( p_playlist );
642 if( p_host->p_songs ) free( p_host->p_songs );