00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <stdlib.h>
00028
00029 #include <vlc/vlc.h>
00030 #include <vlc/intf.h>
00031
00032 #include "network.h"
00033
00034 #include <vlc/input.h>
00035
00036 #include <daap/client.h>
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 static int Open ( vlc_object_t * );
00048 static void Close( vlc_object_t * );
00049 static int OpenAccess ( vlc_object_t * );
00050 static void CloseAccess( vlc_object_t * );
00051
00052 vlc_module_begin();
00053 set_description( _("DAAP shares") );
00054 set_category( CAT_PLAYLIST );
00055 set_subcategory( SUBCAT_PLAYLIST_SD );
00056
00057 set_capability( "services_discovery", 0 );
00058 set_callbacks( Open, Close );
00059
00060 add_submodule();
00061 set_description( _( "DAAP access") );
00062 set_capability( "access2", 0 );
00063 set_callbacks( OpenAccess, CloseAccess );
00064 vlc_module_end();
00065
00066
00067
00068
00069
00070
00071 typedef struct dhost_s {
00072 char *psz_name;
00073 int i_id;
00074
00075 DAAP_SClientHost *p_host;
00076 vlc_bool_t b_updated;
00077 vlc_bool_t b_new;
00078 int i_database_id;
00079
00080 playlist_item_t *p_node;
00081
00082 DAAP_ClientHost_DatabaseItem *p_songs;
00083 int i_songs;
00084 } dhost_t;
00085
00086 typedef struct daap_db_s {
00087 dhost_t **pp_hosts;
00088 int i_hosts;
00089
00090 int i_last_id;
00091
00092 vlc_mutex_t search_lock;
00093 } daap_db_t;
00094
00095 struct services_discovery_sys_t {
00096 playlist_item_t *p_node;
00097
00098 DAAP_SClient *p_client;
00099 DAAP_SClientHost *p_host;
00100
00101 daap_db_t *p_db;
00102 };
00103
00104 struct access_sys_t {
00105 vlc_url_t url;
00106
00107 dhost_t *p_host;
00108 int i_host;
00109 int i_song;
00110
00111 daap_db_t *p_db;
00112
00113 DAAP_ClientHost_Song song;
00114 DAAP_ClientHost_DatabaseItem songdata;
00115 int i_orig_size;
00116 void *p_orig_buffer;
00117 };
00118
00119
00120
00121
00122
00123
00124 static void Run ( services_discovery_t *p_sd );
00125 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
00126 int i_pos, void *p_context );
00127 static int EnumerateCallback( DAAP_SClient *p_client,
00128 DAAP_SClientHost *p_host,
00129 void *p_context );
00130 static void OnHostsUpdate( services_discovery_t *p_sd );
00131 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host );
00132 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host );
00133
00134 static int Control( access_t *p_access, int i_query, va_list args );
00135 static int Read( access_t *, uint8_t *, int );
00136 static int Seek( access_t *, int64_t );
00137
00138
00139
00140
00141 static int Open( vlc_object_t *p_this )
00142 {
00143 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00144 services_discovery_sys_t *p_sys = (services_discovery_sys_t *)
00145 malloc( sizeof( services_discovery_sys_t ) );
00146
00147 playlist_t *p_playlist;
00148 playlist_view_t *p_view;
00149 vlc_value_t val;
00150
00151 p_sd->pf_run = Run;
00152 p_sd->p_sys = p_sys;
00153
00154 p_sys->p_db = (daap_db_t *)malloc( sizeof( daap_db_t ) );
00155 if( !p_sys->p_db )
00156 {
00157 return VLC_EGENERIC;
00158 }
00159 p_sys->p_db->pp_hosts = NULL;
00160 p_sys->p_db->i_hosts = 0;
00161
00162 var_Create( p_sd->p_vlc, "daap-db", VLC_VAR_ADDRESS );
00163 val.p_address = p_sys->p_db;
00164 var_Set( p_sd->p_vlc, "daap-db", val );
00165
00166 vlc_mutex_init( p_sd, &p_sys->p_db->search_lock );
00167
00168
00169 p_sys->p_client = DAAP_Client_Create( Callback, p_sd );
00170 p_sys->p_db->i_last_id = 0;
00171
00172
00173
00174
00175
00176
00177 p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00178 FIND_ANYWHERE );
00179 if( !p_playlist )
00180 {
00181 msg_Warn( p_sd, "unable to find playlist, cancelling DAAP" );
00182 return VLC_EGENERIC;
00183 }
00184
00185 p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
00186 p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
00187 _("DAAP shares"), p_view->p_root );
00188 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
00189
00190 val.b_bool = VLC_TRUE;
00191 var_Set( p_playlist, "intf-change", val );
00192 vlc_object_release( p_playlist );
00193
00194 return VLC_SUCCESS;
00195 }
00196
00197 static int OpenAccess( vlc_object_t *p_this )
00198 {
00199 access_t *p_access = (access_t*)p_this;
00200 access_sys_t *p_sys;
00201 vlc_value_t val;
00202 vlc_bool_t b_found = VLC_FALSE;
00203 int i, i_ret;
00204
00205 p_access->pf_read = Read;
00206 p_access->pf_block = NULL;
00207 p_access->pf_control = Control;
00208 p_access->pf_seek = Seek;
00209 p_access->info.i_update = 0;
00210 p_access->info.i_size = 0;
00211 p_access->info.i_pos = 0;
00212 p_access->info.b_eof = VLC_FALSE;
00213 p_access->info.i_title = 0;
00214 p_access->info.i_seekpoint = 0;
00215 p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
00216 memset( p_sys, 0, sizeof( access_sys_t ) );
00217
00218 i_ret = var_Get( p_access->p_vlc , "daap-db", &val );
00219 p_sys->p_db = val.p_address;
00220
00221 if( p_sys->p_db == NULL || i_ret )
00222 {
00223 msg_Err( p_access, "The DAAP services_discovery module must be enabled" );
00224 return VLC_EGENERIC;
00225 }
00226
00227 vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
00228
00229 p_sys->p_host = NULL;
00230 p_sys->i_host = atoi( p_sys->url.psz_host ) ;
00231 p_sys->i_song = p_sys->url.i_port;
00232
00233 if( !p_sys->i_host || !p_sys->i_song )
00234 {
00235 msg_Err( p_access, "invalid host or song" );
00236 return VLC_EGENERIC;
00237 }
00238
00239
00240 vlc_mutex_lock( &p_sys->p_db->search_lock );
00241 for( i = 0 ; i < p_sys->p_db->i_hosts ; i++ )
00242 {
00243 if( p_sys->p_db->pp_hosts[i]->i_id == p_sys->i_host )
00244 {
00245 p_sys->p_host = p_sys->p_db->pp_hosts[i];
00246 break;
00247 }
00248 }
00249 if( p_sys->p_host )
00250 {
00251 for( i = 0 ; i < p_sys->p_host->i_songs ; i++ )
00252 {
00253 if( p_sys->p_host->p_songs[i].id == p_sys->i_song )
00254 {
00255 p_sys->songdata = p_sys->p_host->p_songs[i];
00256 b_found = VLC_TRUE;
00257 break;
00258 }
00259 }
00260 if( !b_found )
00261 {
00262 msg_Err( p_access, "invalid song (not found in %i)",
00263 p_sys->p_host->i_songs );
00264 }
00265 }
00266 else
00267 {
00268 msg_Warn( p_access, "invalid host (not found in %i)",
00269 p_sys->p_db->i_hosts );
00270 }
00271 vlc_mutex_unlock( &p_sys->p_db->search_lock );
00272
00273 if( !p_sys->p_host || !b_found )
00274 {
00275 return VLC_EGENERIC;
00276 }
00277
00278
00279 msg_Dbg( p_access, "Downloading %s song %i (db %i)",
00280 p_sys->songdata.songformat,
00281 p_sys->i_song, p_sys->p_host->i_database_id );
00282
00283
00284 i_ret = DAAP_ClientHost_GetAudioFile( p_sys->p_host->p_host,
00285 p_sys->p_host->i_database_id,
00286 p_sys->i_song,
00287 p_sys->songdata.songformat,
00288 &(p_sys->song) );
00289
00290 msg_Dbg( p_access, "Finished downloading, read %i bytes (ret %i)",
00291 p_sys->song.size, i_ret );
00292
00293 p_access->info.i_size = p_sys->song.size;
00294
00295 if( i_ret != 0 )
00296 return VLC_EGENERIC;
00297
00298 return VLC_SUCCESS;
00299 }
00300
00301
00302
00303
00304 static void Close( vlc_object_t *p_this )
00305 {
00306 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00307 services_discovery_sys_t *p_sys = p_sd->p_sys;
00308
00309 playlist_t *p_playlist;
00310 int i;
00311
00312 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00313 FIND_ANYWHERE );
00314
00315 for( i = 0 ; i< p_sys->p_db->i_hosts ; i++ )
00316 {
00317 FreeHost( p_sd, p_sys->p_db->pp_hosts[i] );
00318 }
00319
00320 var_Destroy( p_sd->p_vlc, "daap-db" );
00321
00322 if( p_playlist )
00323 {
00324 playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
00325 vlc_object_release( p_playlist );
00326 }
00327
00328 free( p_sys );
00329 }
00330
00331 static void CloseAccess( vlc_object_t *p_this )
00332 {
00333 access_t *p_access = (access_t*) p_this;
00334 access_sys_t *p_sys = p_access->p_sys;
00335
00336 if( p_sys )
00337 {
00338 if( p_sys->p_host )
00339 {
00340 p_sys->song.data = p_sys->p_orig_buffer;
00341 p_sys->song.size = p_sys->i_orig_size;
00342 DAAP_ClientHost_FreeAudioFile( p_sys->p_host->p_host, &p_sys->song );
00343 }
00344 free( p_sys );
00345 }
00346 }
00347
00348
00349
00350
00351 static void Run( services_discovery_t *p_sd )
00352 {
00353 while( !p_sd->b_die )
00354 {
00355 msleep( 100000 );
00356 }
00357 }
00358
00359
00360
00361
00362 static int Control( access_t *p_access, int i_query, va_list args )
00363 {
00364 vlc_bool_t *pb_bool;
00365 int64_t *pi_64;
00366 switch( i_query )
00367 {
00368 case ACCESS_CAN_SEEK:
00369 case ACCESS_CAN_FASTSEEK:
00370 pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
00371 *pb_bool = VLC_TRUE;
00372 break;
00373 case ACCESS_CAN_PAUSE:
00374 case ACCESS_CAN_CONTROL_PACE:
00375 pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
00376 *pb_bool = VLC_TRUE;
00377 break;
00378
00379 case ACCESS_GET_PTS_DELAY:
00380 pi_64 = (int64_t *)va_arg( args, int64_t *);
00381 *pi_64 = (int64_t)300000;
00382 break;
00383
00384 case ACCESS_SET_PAUSE_STATE:
00385 break;
00386
00387 case ACCESS_GET_TITLE_INFO:
00388 case ACCESS_SET_TITLE:
00389 case ACCESS_SET_SEEKPOINT:
00390 case ACCESS_SET_PRIVATE_ID_STATE:
00391 return VLC_EGENERIC;
00392
00393 default:
00394 msg_Warn( p_access, "unimplemented query control %i", i_query );
00395 return VLC_EGENERIC;
00396 }
00397 return VLC_SUCCESS;
00398 }
00399
00400 static int Read( access_t *p_access, uint8_t *p_buffer, int i_size )
00401 {
00402 access_sys_t *p_sys = (access_sys_t *)p_access->p_sys;
00403 int i_send;
00404
00405 if( i_size < p_sys->song.size && p_sys->song.size > 0 )
00406 {
00407 i_send = i_size;
00408 }
00409 else if( p_sys->song.size == 0 )
00410 {
00411 return 0;
00412 }
00413 else
00414 {
00415 i_send = p_sys->song.size;
00416 }
00417
00418 memcpy( p_buffer, p_sys->song.data, i_send );
00419 p_sys->song.size -= i_send;
00420 p_sys->song.data += i_send;
00421
00422 return i_send;
00423 }
00424
00425 static int Seek( access_t *p_access, int64_t i_pos )
00426 {
00427 if( i_pos > p_access->p_sys->i_orig_size )
00428 {
00429 return VLC_EGENERIC;
00430 }
00431 p_access->p_sys->song.size = p_access->p_sys->i_orig_size - i_pos;
00432 p_access->p_sys->song.data = p_access->p_sys->p_orig_buffer + i_pos;
00433 return VLC_SUCCESS;
00434 }
00435
00436
00437
00438
00439 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
00440 int i_pos, void *p_context )
00441 {
00442 services_discovery_t *p_sd = (services_discovery_t *)p_context;
00443
00444 if( status == DAAP_STATUS_hostschanged )
00445 {
00446 OnHostsUpdate( p_sd );
00447 }
00448 else if( status == DAAP_STATUS_downloading )
00449 {
00450 }
00451 }
00452
00453 static void OnHostsUpdate( services_discovery_t *p_sd )
00454 {
00455 int i;
00456
00457 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
00458 {
00459 p_sd->p_sys->p_db->pp_hosts[i]->b_updated = VLC_FALSE;
00460 p_sd->p_sys->p_db->pp_hosts[i]->b_new = VLC_FALSE;
00461 }
00462
00463 vlc_mutex_lock( &p_sd->p_sys->p_db->search_lock );
00464 DAAP_Client_EnumerateHosts( p_sd->p_sys->p_client, EnumerateCallback, p_sd);
00465
00466 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
00467 {
00468 if( p_sd->p_sys->p_db->pp_hosts[i]->b_updated == VLC_FALSE )
00469 {
00470 dhost_t *p_host = p_sd->p_sys->p_db->pp_hosts[i];
00471 FreeHost( p_sd, p_host );
00472 REMOVE_ELEM( p_sd->p_sys->p_db->pp_hosts,
00473 p_sd->p_sys->p_db->i_hosts, i );
00474 }
00475 }
00476 vlc_mutex_unlock( &p_sd->p_sys->p_db->search_lock );
00477
00478 for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
00479 {
00480 if( p_sd->p_sys->p_db->pp_hosts[i]->b_new )
00481 ProcessHost( p_sd, p_sd->p_sys->p_db->pp_hosts[i] );
00482 }
00483 }
00484
00485 static int EnumerateCallback( DAAP_SClient *p_client,
00486 DAAP_SClientHost *p_host,
00487 void *p_context )
00488 {
00489 int i;
00490 int i_size = DAAP_ClientHost_GetSharename( p_host, NULL, 0 );
00491 vlc_bool_t b_found = VLC_FALSE;
00492 char *psz_buffer = (char *)malloc( i_size );
00493 DAAP_ClientHost_GetSharename( p_host, psz_buffer, i_size );
00494
00495 services_discovery_t *p_sd = (services_discovery_t *)p_context;
00496 services_discovery_sys_t *p_sys = p_sd->p_sys;
00497
00498 for( i = 0 ; i< p_sys->p_db->i_hosts; i++ )
00499 {
00500 if( !strcmp( p_sys->p_db->pp_hosts[i]->psz_name, psz_buffer ) )
00501 {
00502 p_sys->p_db->pp_hosts[i]->b_updated = VLC_TRUE;
00503 b_found = VLC_TRUE;
00504 break;
00505 }
00506 }
00507
00508 if( !b_found )
00509 {
00510 dhost_t *p_vlchost = (dhost_t *)malloc( sizeof( dhost_t ) );
00511 p_vlchost->p_node = NULL;
00512 p_vlchost->p_host = p_host;
00513 p_vlchost->psz_name = psz_buffer;
00514 p_vlchost->b_new = VLC_TRUE;
00515 p_vlchost->b_updated = VLC_TRUE;
00516 INSERT_ELEM( p_sys->p_db->pp_hosts, p_sys->p_db->i_hosts,
00517 p_sys->p_db->i_hosts, p_vlchost );
00518 }
00519
00520 return VLC_SUCCESS;
00521 }
00522
00523 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host )
00524 {
00525 int i_dbsize, i_db, i, i_songsize, i_ret;
00526 int i_size = DAAP_ClientHost_GetSharename( p_host->p_host, NULL, 0 );
00527
00528 playlist_t *p_playlist;
00529
00530 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00531 FIND_ANYWHERE );
00532
00533 if( !p_playlist )
00534 {
00535 return;
00536 }
00537
00538
00539 if( p_host->b_new )
00540 {
00541 p_host->psz_name = (char *)malloc( i_size );
00542 p_host->b_new = VLC_FALSE;
00543 DAAP_ClientHost_GetSharename( p_host->p_host, p_host->psz_name ,
00544 i_size );
00545
00546 msg_Dbg( p_sd, "new share %s", p_host->psz_name );
00547 DAAP_ClientHost_AddRef( p_host->p_host );
00548 i_ret = DAAP_ClientHost_Connect( p_host->p_host );
00549 if( i_ret )
00550 {
00551 msg_Warn( p_sd, "unable to connect to DAAP host %s",
00552 p_host->psz_name );
00553
00554 vlc_object_release( p_playlist );
00555 return;
00556 }
00557
00558 p_host->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
00559 p_host->psz_name,
00560 p_sd->p_sys->p_node );
00561 p_host->i_id = ++p_sd->p_sys->p_db->i_last_id;
00562 }
00563
00564
00565 i_dbsize = DAAP_ClientHost_GetDatabases( p_host->p_host, NULL, NULL, 0 );
00566
00567 DAAP_ClientHost_Database *p_database = malloc( i_dbsize );
00568 DAAP_ClientHost_GetDatabases( p_host->p_host, p_database, &i_db, i_dbsize );
00569
00570
00571 if( !i_db || !p_database )
00572 {
00573 msg_Warn( p_sd, "no database on DAAP host %s", p_host->psz_name );
00574 vlc_object_release( p_playlist );
00575 return;
00576 }
00577
00578
00579 p_host->i_database_id = p_database[0].id;
00580
00581
00582 i_songsize = DAAP_ClientHost_GetDatabaseItems( p_host->p_host,
00583 p_host->i_database_id,
00584 NULL, NULL, 0 );
00585 if( !i_songsize )
00586 {
00587 vlc_object_release( p_playlist );
00588 return;
00589 }
00590 p_host->p_songs = malloc( i_songsize );
00591
00592 DAAP_ClientHost_GetDatabaseItems( p_host->p_host ,
00593 p_host->i_database_id,
00594 p_host->p_songs,
00595 &p_host->i_songs, i_songsize );
00596
00597 for( i = 0; i< p_host->i_songs; i++ )
00598 {
00599 playlist_item_t *p_item;
00600 int i_len = 7 + 10 + 1 + 10 ;
00601 char *psz_buff = (char *)malloc( i_len );
00602
00603 snprintf( psz_buff, i_len, "daap://%i:%i", p_host->i_id,
00604 p_host->p_songs[i].id );
00605 p_item = playlist_ItemNew( p_sd, psz_buff,
00606 p_host->p_songs[i].itemname );
00607 vlc_input_item_AddInfo( &p_item->input, _("Meta-information"),
00608 _("Artist"), p_host->p_songs[i].songartist );
00609 vlc_input_item_AddInfo( &p_item->input, _("Meta-information"),
00610 _("Album"), p_host->p_songs[i].songalbum );
00611
00612 playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY,
00613 p_host->p_node, PLAYLIST_APPEND, PLAYLIST_END );
00614
00615 }
00616
00617 DAAP_ClientHost_AsyncWaitUpdate( p_host->p_host );
00618
00619 vlc_object_release( p_playlist );
00620 }
00621
00622 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host )
00623 {
00624 playlist_t *p_playlist;
00625
00626 if( p_host->p_host )
00627 {
00628 DAAP_ClientHost_Disconnect( p_host->p_host );
00629 DAAP_ClientHost_Release( p_host->p_host );
00630 }
00631
00632 p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00633 FIND_ANYWHERE );
00634 if( p_playlist )
00635 {
00636 if( p_host->p_node )
00637 playlist_NodeDelete( p_playlist, p_host->p_node, VLC_TRUE ,
00638 VLC_TRUE);
00639 vlc_object_release( p_playlist );
00640 }
00641
00642 if( p_host->p_songs ) free( p_host->p_songs );
00643 }