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_libvlc, "daap-db", VLC_VAR_ADDRESS );
163 val.p_address = p_sys->p_db;
164 var_Set( p_sd->p_libvlc, "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" );
184 msg_Err( p_sd, "DAAP IS BROKEN !! Fix it if you want it !" );
188 p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
189 _("DAAP shares"), p_view->p_root );
190 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
192 val.b_bool = VLC_TRUE;
193 var_Set( p_playlist, "intf-change", val );
194 vlc_object_release( p_playlist );
200 static int OpenAccess( vlc_object_t *p_this )
203 access_t *p_access = (access_t*)p_this;
206 vlc_bool_t b_found = VLC_FALSE;
209 p_access->pf_read = Read;
210 p_access->pf_block = NULL;
211 p_access->pf_control = Control;
212 p_access->pf_seek = Seek;
213 p_access->info.i_update = 0;
214 p_access->info.i_size = 0;
215 p_access->info.i_pos = 0;
216 p_access->info.b_eof = VLC_FALSE;
217 p_access->info.i_title = 0;
218 p_access->info.i_seekpoint = 0;
219 p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
220 memset( p_sys, 0, sizeof( access_sys_t ) );
222 i_ret = var_Get( p_access->p_libvlc , "daap-db", &val );
223 p_sys->p_db = val.p_address;
225 if( p_sys->p_db == NULL || i_ret )
227 msg_Err( p_access, "the DAAP services_discovery module must be enabled" );
231 vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
233 p_sys->p_host = NULL;
234 p_sys->i_host = atoi( p_sys->url.psz_host ) ;
235 p_sys->i_song = p_sys->url.i_port;
237 if( !p_sys->i_host || !p_sys->i_song )
239 msg_Err( p_access, "invalid host or song" );
243 /* Search the host */
244 vlc_mutex_lock( &p_sys->p_db->search_lock );
245 for( i = 0 ; i < p_sys->p_db->i_hosts ; i++ )
247 if( p_sys->p_db->pp_hosts[i]->i_id == p_sys->i_host )
249 p_sys->p_host = p_sys->p_db->pp_hosts[i];
255 for( i = 0 ; i < p_sys->p_host->i_songs ; i++ )
257 if( p_sys->p_host->p_songs[i].id == p_sys->i_song )
259 p_sys->songdata = p_sys->p_host->p_songs[i];
266 msg_Err( p_access, "invalid song (not found in %i)",
267 p_sys->p_host->i_songs );
272 msg_Warn( p_access, "invalid host (not found in %i)",
273 p_sys->p_db->i_hosts );
275 vlc_mutex_unlock( &p_sys->p_db->search_lock );
277 if( !p_sys->p_host || !b_found )
283 msg_Dbg( p_access, "downloading %s song %i (db %i)",
284 p_sys->songdata.songformat,
285 p_sys->i_song, p_sys->p_host->i_database_id );
287 /* FIXME: wait for better method by upstream */
288 i_ret = DAAP_ClientHost_GetAudioFile( p_sys->p_host->p_host,
289 p_sys->p_host->i_database_id,
291 p_sys->songdata.songformat,
294 msg_Dbg( p_access, "finished downloading, read %i bytes (ret %i)",
295 p_sys->song.size, i_ret );
297 p_access->info.i_size = p_sys->song.size;
305 /*****************************************************************************
307 *****************************************************************************/
308 static void Close( vlc_object_t *p_this )
311 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
312 services_discovery_sys_t *p_sys = p_sd->p_sys;
314 playlist_t *p_playlist;
317 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
320 for( i = 0 ; i< p_sys->p_db->i_hosts ; i++ )
322 FreeHost( p_sd, p_sys->p_db->pp_hosts[i] );
325 var_Destroy( p_sd->p_libvlc, "daap-db" );
329 playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
330 vlc_object_release( p_playlist );
337 static void CloseAccess( vlc_object_t *p_this )
339 access_t *p_access = (access_t*) p_this;
340 access_sys_t *p_sys = p_access->p_sys;
346 p_sys->song.data = p_sys->p_orig_buffer;
347 p_sys->song.size = p_sys->i_orig_size;
348 DAAP_ClientHost_FreeAudioFile( p_sys->p_host->p_host, &p_sys->song );
354 /*****************************************************************************
355 * Run: main DAAP thread
356 *****************************************************************************/
357 static void Run( services_discovery_t *p_sd )
359 while( !p_sd->b_die )
365 /*****************************************************************************
367 *****************************************************************************/
368 static int Control( access_t *p_access, int i_query, va_list args )
374 case ACCESS_CAN_SEEK:
375 case ACCESS_CAN_FASTSEEK:
376 pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
379 case ACCESS_CAN_PAUSE:
380 case ACCESS_CAN_CONTROL_PACE:
381 pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
385 case ACCESS_GET_PTS_DELAY:
386 pi_64 = (int64_t *)va_arg( args, int64_t *);
387 *pi_64 = (int64_t)300000;
390 case ACCESS_SET_PAUSE_STATE:
393 case ACCESS_GET_TITLE_INFO:
394 case ACCESS_SET_TITLE:
395 case ACCESS_SET_SEEKPOINT:
396 case ACCESS_SET_PRIVATE_ID_STATE:
400 msg_Warn( p_access, "unimplemented query control %i", i_query );
406 static int Read( access_t *p_access, uint8_t *p_buffer, int i_size )
408 access_sys_t *p_sys = (access_sys_t *)p_access->p_sys;
411 if( i_size < p_sys->song.size && p_sys->song.size > 0 )
415 else if( p_sys->song.size == 0 )
421 i_send = p_sys->song.size;
424 memcpy( p_buffer, p_sys->song.data, i_send );
425 p_sys->song.size -= i_send;
426 p_sys->song.data += i_send;
431 static int Seek( access_t *p_access, int64_t i_pos )
433 if( i_pos > p_access->p_sys->i_orig_size )
437 p_access->p_sys->song.size = p_access->p_sys->i_orig_size - i_pos;
438 p_access->p_sys->song.data = p_access->p_sys->p_orig_buffer + i_pos;
442 /**************************************************************
444 **************************************************************/
445 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
446 int i_pos, void *p_context )
448 services_discovery_t *p_sd = (services_discovery_t *)p_context;
450 if( status == DAAP_STATUS_hostschanged )
452 OnHostsUpdate( p_sd );
454 else if( status == DAAP_STATUS_downloading )
459 static void OnHostsUpdate( services_discovery_t *p_sd )
463 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
465 p_sd->p_sys->p_db->pp_hosts[i]->b_updated = VLC_FALSE;
466 p_sd->p_sys->p_db->pp_hosts[i]->b_new = VLC_FALSE;
469 vlc_mutex_lock( &p_sd->p_sys->p_db->search_lock );
470 DAAP_Client_EnumerateHosts( p_sd->p_sys->p_client, EnumerateCallback, p_sd);
472 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
474 if( p_sd->p_sys->p_db->pp_hosts[i]->b_updated == VLC_FALSE )
476 dhost_t *p_host = p_sd->p_sys->p_db->pp_hosts[i];
477 FreeHost( p_sd, p_host );
478 REMOVE_ELEM( p_sd->p_sys->p_db->pp_hosts,
479 p_sd->p_sys->p_db->i_hosts, i );
482 vlc_mutex_unlock( &p_sd->p_sys->p_db->search_lock );
484 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
486 if( p_sd->p_sys->p_db->pp_hosts[i]->b_new )
487 ProcessHost( p_sd, p_sd->p_sys->p_db->pp_hosts[i] );
491 static int EnumerateCallback( DAAP_SClient *p_client,
492 DAAP_SClientHost *p_host,
496 int i_size = DAAP_ClientHost_GetSharename( p_host, NULL, 0 );
497 vlc_bool_t b_found = VLC_FALSE;
498 char *psz_buffer = (char *)malloc( i_size );
499 DAAP_ClientHost_GetSharename( p_host, psz_buffer, i_size );
501 services_discovery_t *p_sd = (services_discovery_t *)p_context;
502 services_discovery_sys_t *p_sys = p_sd->p_sys;
504 for( i = 0 ; i< p_sys->p_db->i_hosts; i++ )
506 if( !strcmp( p_sys->p_db->pp_hosts[i]->psz_name, psz_buffer ) )
508 p_sys->p_db->pp_hosts[i]->b_updated = VLC_TRUE;
516 dhost_t *p_vlchost = (dhost_t *)malloc( sizeof( dhost_t ) );
517 p_vlchost->p_node = NULL;
518 p_vlchost->p_host = p_host;
519 p_vlchost->psz_name = psz_buffer;
520 p_vlchost->b_new = VLC_TRUE;
521 p_vlchost->b_updated = VLC_TRUE;
522 INSERT_ELEM( p_sys->p_db->pp_hosts, p_sys->p_db->i_hosts,
523 p_sys->p_db->i_hosts, p_vlchost );
529 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host )
532 int i_dbsize, i_db, i, i_songsize, i_ret;
533 int i_size = DAAP_ClientHost_GetSharename( p_host->p_host, NULL, 0 );
535 playlist_t *p_playlist;
537 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
545 /* Connect to host */
548 p_host->psz_name = (char *)malloc( i_size );
549 p_host->b_new = VLC_FALSE;
550 DAAP_ClientHost_GetSharename( p_host->p_host, p_host->psz_name ,
553 msg_Dbg( p_sd, "new share %s", p_host->psz_name );
554 DAAP_ClientHost_AddRef( p_host->p_host );
555 i_ret = DAAP_ClientHost_Connect( p_host->p_host );
558 msg_Warn( p_sd, "unable to connect to DAAP host %s",
560 // DAAP_ClientHost_Release( p_host->p_host );
561 vlc_object_release( p_playlist );
565 p_host->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
567 p_sd->p_sys->p_node );
568 p_host->i_id = ++p_sd->p_sys->p_db->i_last_id;
572 i_dbsize = DAAP_ClientHost_GetDatabases( p_host->p_host, NULL, NULL, 0 );
574 DAAP_ClientHost_Database *p_database = malloc( i_dbsize );
575 DAAP_ClientHost_GetDatabases( p_host->p_host, p_database, &i_db, i_dbsize );
578 if( !i_db || !p_database )
580 msg_Warn( p_sd, "no database on DAAP host %s", p_host->psz_name );
581 vlc_object_release( p_playlist );
585 /* We only use the first database */
586 p_host->i_database_id = p_database[0].id;
589 i_songsize = DAAP_ClientHost_GetDatabaseItems( p_host->p_host,
590 p_host->i_database_id,
594 vlc_object_release( p_playlist );
597 p_host->p_songs = malloc( i_songsize );
599 DAAP_ClientHost_GetDatabaseItems( p_host->p_host ,
600 p_host->i_database_id,
602 &p_host->i_songs, i_songsize );
604 for( i = 0; i< p_host->i_songs; i++ )
606 playlist_item_t *p_item;
607 int i_len = 7 + 10 + 1 + 10 ; /* "daap://" + host + ":" + song */
608 char *psz_buff = (char *)malloc( i_len );
610 snprintf( psz_buff, i_len, "daap://%i:%i", p_host->i_id,
611 p_host->p_songs[i].id );
612 p_item = playlist_ItemNew( p_sd, psz_buff,
613 p_host->p_songs[i].itemname );
614 input_ItemAddInfo( &p_item->input, _(VLC_META_INFO_CAT),
615 _(VLC_META_ARTIST), p_host->p_songs[i].songartist );
616 input_ItemAddInfo( &p_item->input, _(VLC_META_INFO_CAT),
617 _(VLC_META_COLLECTION), p_host->p_songs[i].songalbum );
619 playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY,
620 p_host->p_node, PLAYLIST_APPEND, PLAYLIST_END );
624 DAAP_ClientHost_AsyncWaitUpdate( p_host->p_host );
626 vlc_object_release( p_playlist );
630 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host )
632 playlist_t *p_playlist;
636 DAAP_ClientHost_Disconnect( p_host->p_host );
637 DAAP_ClientHost_Release( p_host->p_host );
640 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
645 playlist_NodeDelete( p_playlist, p_host->p_node, VLC_TRUE ,
647 vlc_object_release( p_playlist );
650 if( p_host->p_songs ) free( p_host->p_songs );