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
00028 #include <vlc/vlc.h>
00029 #include <vlc/input.h>
00030 #include <vlc_playlist.h>
00031
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #ifdef HAVE_SYS_TYPES_H
00035 # include <sys/types.h>
00036 #endif
00037 #ifdef HAVE_SYS_STAT_H
00038 # include <sys/stat.h>
00039 #endif
00040 #ifdef HAVE_ERRNO_H
00041 # include <errno.h>
00042 #endif
00043 #ifdef HAVE_FCNTL_H
00044 # include <fcntl.h>
00045 #endif
00046
00047 #ifdef HAVE_UNISTD_H
00048 # include <unistd.h>
00049 #elif defined( WIN32 ) && !defined( UNDER_CE )
00050 # include <io.h>
00051 #elif defined( UNDER_CE )
00052 # define strcoll strcmp
00053 #endif
00054
00055 #ifdef HAVE_DIRENT_H
00056 # include <dirent.h>
00057 #endif
00058
00059 #include "charset.h"
00060
00061
00062
00063
00064 static int Open ( vlc_object_t * );
00065 static void Close( vlc_object_t * );
00066
00067 static int DemuxOpen ( vlc_object_t * );
00068
00069 #define RECURSIVE_TEXT N_("Subdirectory behavior")
00070 #define RECURSIVE_LONGTEXT N_( \
00071 "Select whether subdirectories must be expanded.\n" \
00072 "none: subdirectories do not appear in the playlist.\n" \
00073 "collapse: subdirectories appear but are expanded on first play.\n" \
00074 "expand: all subdirectories are expanded.\n" )
00075
00076 static char *psz_recursive_list[] = { "none", "collapse", "expand" };
00077 static char *psz_recursive_list_text[] = { N_("none"), N_("collapse"),
00078 N_("expand") };
00079
00080 #define IGNORE_TEXT N_("Ignore files with these extensions")
00081 #define IGNORE_LONGTEXT N_( \
00082 "Specify a comma seperated list of file extensions. " \
00083 "Files with these extensions will not be added to playlist when opening a directory. " \
00084 "This is useful if you add directories that contain mp3 albums for instance." )
00085
00086 vlc_module_begin();
00087 set_category( CAT_INPUT );
00088 set_shortname( _("Directory" ) );
00089 set_subcategory( SUBCAT_INPUT_ACCESS );
00090 set_description( _("Standard filesystem directory input") );
00091 set_capability( "access2", 55 );
00092 add_shortcut( "directory" );
00093 add_shortcut( "dir" );
00094 add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
00095 RECURSIVE_LONGTEXT, VLC_FALSE );
00096 change_string_list( psz_recursive_list, psz_recursive_list_text, 0 );
00097 add_string( "ignore-filetypes", "m3u,db,nfo,jpg,gif,sfv,txt,sub,idx,srt,cue",
00098 NULL, IGNORE_TEXT, IGNORE_LONGTEXT, VLC_FALSE );
00099 set_callbacks( Open, Close );
00100
00101 add_submodule();
00102 set_description( "Directory EOF");
00103 set_capability( "demux2", 0 );
00104 add_shortcut( "directory" );
00105 set_callbacks( DemuxOpen, NULL );
00106 vlc_module_end();
00107
00108
00109
00110
00111
00112
00113 #define MODE_EXPAND 0
00114 #define MODE_COLLAPSE 1
00115 #define MODE_NONE 2
00116
00117 static int Read( access_t *, uint8_t *, int );
00118 static int ReadNull( access_t *, uint8_t *, int );
00119 static int Control( access_t *, int, va_list );
00120
00121 static int Demux( demux_t *p_demux );
00122 static int DemuxControl( demux_t *p_demux, int i_query, va_list args );
00123
00124
00125 static int ReadDir( playlist_t *, char *psz_name, int i_mode, int *pi_pos,
00126 playlist_item_t * );
00127
00128
00129
00130
00131 static int Open( vlc_object_t *p_this )
00132 {
00133 access_t *p_access = (access_t*)p_this;
00134
00135 #ifdef HAVE_SYS_STAT_H
00136 struct stat stat_info;
00137 char *psz_path = ToLocale( p_access->psz_path );
00138
00139 if( ( stat( psz_path, &stat_info ) == -1 ) ||
00140 !S_ISDIR( stat_info.st_mode ) )
00141 #elif defined(WIN32)
00142 int i_ret;
00143
00144 # ifdef UNICODE
00145 wchar_t psz_path[MAX_PATH];
00146 mbstowcs( psz_path, p_access->psz_path, MAX_PATH );
00147 psz_path[MAX_PATH-1] = 0;
00148 # else
00149 char *psz_path = p_access->psz_path;
00150 # endif
00151
00152 i_ret = GetFileAttributes( psz_path );
00153 if( i_ret == -1 || !(i_ret & FILE_ATTRIBUTE_DIRECTORY) )
00154
00155 #else
00156 if( strcmp( p_access->psz_access, "dir") &&
00157 strcmp( p_access->psz_access, "directory") )
00158 #endif
00159 {
00160 LocaleFree( psz_path );
00161 return VLC_EGENERIC;
00162 }
00163
00164 LocaleFree( psz_path );
00165 p_access->pf_read = Read;
00166 p_access->pf_block = NULL;
00167 p_access->pf_seek = NULL;
00168 p_access->pf_control= Control;
00169
00170
00171 p_access->psz_demux = strdup( "directory" );
00172
00173 return VLC_SUCCESS;
00174 }
00175
00176
00177
00178
00179 static void Close( vlc_object_t * p_this )
00180 {
00181 }
00182
00183
00184
00185
00186 static int ReadNull( access_t *p_access, uint8_t *p_buffer, int i_len)
00187 {
00188
00189 memset( p_buffer, 0, i_len );
00190 return i_len;
00191 }
00192
00193
00194
00195
00196 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len)
00197 {
00198 char *psz_name = NULL;
00199 char *psz;
00200 int i_mode, i_pos;
00201
00202 playlist_item_t *p_item;
00203 vlc_bool_t b_play = VLC_FALSE;
00204
00205 playlist_t *p_playlist =
00206 (playlist_t *) vlc_object_find( p_access,
00207 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
00208
00209 if( !p_playlist )
00210 {
00211 msg_Err( p_access, "can't find playlist" );
00212 goto end;
00213 }
00214 else
00215 {
00216 char *ptr;
00217
00218 psz_name = ToLocale( p_access->psz_path );
00219 ptr = strdup( psz_name );
00220 LocaleFree( psz_name );
00221 if( ptr == NULL )
00222 goto end;
00223
00224 psz_name = ptr;
00225
00226
00227 ptr += strlen( ptr );
00228 if( ( ptr > psz_name ) )
00229 {
00230 switch( *--ptr )
00231 {
00232 case '/':
00233 case '\\':
00234 *ptr = '\0';
00235 }
00236 }
00237 }
00238
00239
00240 psz = var_CreateGetString( p_access, "recursive" );
00241 if( *psz == '\0' || !strncmp( psz, "none" , 4 ) )
00242 {
00243 i_mode = MODE_NONE;
00244 }
00245 else if( !strncmp( psz, "collapse", 8 ) )
00246 {
00247 i_mode = MODE_COLLAPSE;
00248 }
00249 else
00250 {
00251 i_mode = MODE_EXPAND;
00252 }
00253 free( psz );
00254
00255
00256
00257 i_pos = p_playlist->i_index + 1;
00258
00259 msg_Dbg( p_access, "opening directory `%s'", p_access->psz_path );
00260
00261 if( &p_playlist->status.p_item->input ==
00262 ((input_thread_t *)p_access->p_parent)->input.p_item )
00263 {
00264 p_item = p_playlist->status.p_item;
00265 b_play = VLC_TRUE;
00266 msg_Dbg( p_access, "starting directory playback");
00267 }
00268 else
00269 {
00270 input_item_t *p_current = ( (input_thread_t*)p_access->p_parent)->
00271 input.p_item;
00272 p_item = playlist_LockItemGetByInput( p_playlist, p_current );
00273 msg_Dbg( p_access, "not starting directory playback");
00274 if( !p_item )
00275 {
00276 msg_Dbg( p_playlist, "unable to find item in playlist");
00277 return -1;
00278 }
00279 b_play = VLC_FALSE;
00280 }
00281
00282 p_item->input.i_type = ITEM_TYPE_DIRECTORY;
00283 if( ReadDir( p_playlist, psz_name , i_mode, &i_pos,
00284 p_item ) != VLC_SUCCESS )
00285 {
00286 }
00287 end:
00288
00289
00290 if( b_play )
00291 {
00292 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
00293 p_playlist->status.i_view,
00294 p_playlist->status.p_item, NULL );
00295 }
00296 if( psz_name ) free( psz_name );
00297 vlc_object_release( p_playlist );
00298
00299
00300 p_access->pf_read = ReadNull;
00301 return ReadNull( p_access, p_buffer, i_len );
00302 }
00303
00304
00305
00306
00307 static int Control( access_t *p_access, int i_query, va_list args )
00308 {
00309 vlc_bool_t *pb_bool;
00310 int *pi_int;
00311 int64_t *pi_64;
00312
00313 switch( i_query )
00314 {
00315
00316 case ACCESS_CAN_SEEK:
00317 case ACCESS_CAN_FASTSEEK:
00318 case ACCESS_CAN_PAUSE:
00319 case ACCESS_CAN_CONTROL_PACE:
00320 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00321 *pb_bool = VLC_FALSE;
00322 break;
00323
00324
00325 case ACCESS_GET_MTU:
00326 pi_int = (int*)va_arg( args, int * );
00327 *pi_int = 0;
00328 break;
00329
00330 case ACCESS_GET_PTS_DELAY:
00331 pi_64 = (int64_t*)va_arg( args, int64_t * );
00332 *pi_64 = DEFAULT_PTS_DELAY * 1000;
00333 break;
00334
00335
00336 case ACCESS_SET_PAUSE_STATE:
00337 case ACCESS_GET_TITLE_INFO:
00338 case ACCESS_SET_TITLE:
00339 case ACCESS_SET_SEEKPOINT:
00340 case ACCESS_SET_PRIVATE_ID_STATE:
00341 return VLC_EGENERIC;
00342
00343 default:
00344 msg_Warn( p_access, "unimplemented query in control" );
00345 return VLC_EGENERIC;
00346 }
00347 return VLC_SUCCESS;
00348 }
00349
00350
00351
00352
00353 static int DemuxOpen ( vlc_object_t *p_this )
00354 {
00355 demux_t *p_demux = (demux_t*)p_this;
00356
00357 if( strcmp( p_demux->psz_demux, "directory" ) )
00358 return VLC_EGENERIC;
00359
00360 p_demux->pf_demux = Demux;
00361 p_demux->pf_control = DemuxControl;
00362 return VLC_SUCCESS;
00363 }
00364
00365
00366
00367
00368 static int Demux( demux_t *p_demux )
00369 {
00370 return 0;
00371 }
00372
00373
00374
00375 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
00376 {
00377 return demux2_vaControlHelper( p_demux->s, 0, 0, 0, 1, i_query, args );
00378 }
00379
00380 static int Filter( const struct dirent *foo )
00381 {
00382 return VLC_TRUE;
00383 }
00384
00385
00386
00387 static int ReadDir( playlist_t *p_playlist,
00388 char *psz_name, int i_mode, int *pi_position,
00389 playlist_item_t *p_parent )
00390 {
00391 struct dirent **pp_dir_content;
00392 int i_dir_content, i;
00393 playlist_item_t *p_node;
00394
00395
00396 char **ppsz_extensions = 0;
00397 int i_extensions = 0;
00398 char *psz_ignore = var_CreateGetString( p_playlist, "ignore-filetypes" );
00399 if( psz_ignore && *psz_ignore )
00400 {
00401 char *psz_parser = psz_ignore;
00402 int a;
00403
00404 for( a = 0; psz_parser[a] != '\0'; a++ )
00405 {
00406 if( psz_parser[a] == ',' ) i_extensions++;
00407 }
00408
00409 ppsz_extensions = (char **)malloc( sizeof( char * ) * i_extensions );
00410
00411 for( a = 0; a < i_extensions; a++ )
00412 {
00413 int b;
00414 char *tmp;
00415
00416 while( psz_parser[0] != '\0' && psz_parser[0] == ' ' ) psz_parser++;
00417 for( b = 0; psz_parser[b] != '\0'; b++ )
00418 {
00419 if( psz_parser[b] == ',' ) break;
00420 }
00421 tmp = malloc( b + 1 );
00422 strncpy( tmp, psz_parser, b );
00423 tmp[b] = 0;
00424 ppsz_extensions[a] = tmp;
00425 psz_parser += b+1;
00426 }
00427 }
00428
00429
00430 if( p_parent->i_children == -1 )
00431 {
00432 playlist_LockItemToNode( p_playlist,p_parent );
00433 }
00434
00435
00436 i_dir_content = scandir( psz_name, &pp_dir_content, Filter, alphasort );
00437 if( i_dir_content == -1 )
00438 {
00439 msg_Warn( p_playlist, "Failed to read directory" );
00440 return VLC_EGENERIC;
00441 }
00442 else if( i_dir_content <= 0 )
00443 {
00444
00445 return VLC_SUCCESS;
00446 }
00447
00448
00449 for( i = 0; i < i_dir_content; i++ )
00450 {
00451 struct dirent *p_dir_content = pp_dir_content[i];
00452 int i_size_entry = strlen( psz_name ) +
00453 strlen( p_dir_content->d_name ) + 2;
00454 char *psz_uri = (char *)malloc( sizeof(char) * i_size_entry );
00455
00456 sprintf( psz_uri, "%s/%s", psz_name, p_dir_content->d_name );
00457
00458
00459 if( p_dir_content->d_name[0] != '.' )
00460 {
00461 #if defined( S_ISDIR )
00462 struct stat stat_data;
00463
00464 if( !stat( psz_uri, &stat_data )
00465 && S_ISDIR(stat_data.st_mode) && i_mode != MODE_COLLAPSE )
00466 #elif defined( DT_DIR )
00467 if( ( p_dir_content->d_type & DT_DIR ) && i_mode != MODE_COLLAPSE )
00468 #else
00469 if( 0 )
00470 #endif
00471 {
00472 if( i_mode == MODE_NONE )
00473 {
00474 msg_Dbg( p_playlist, "Skipping subdirectory %s", psz_uri );
00475 free( psz_uri );
00476 continue;
00477 }
00478 else if( i_mode == MODE_EXPAND )
00479 {
00480 char *psz_newname, *psz_tmp;
00481 msg_Dbg(p_playlist, "Reading subdirectory %s", psz_uri );
00482
00483 psz_tmp = FromLocale( p_dir_content->d_name );
00484 psz_newname = vlc_fix_readdir_charset(
00485 p_playlist, psz_tmp );
00486 LocaleFree( psz_tmp );
00487
00488 p_node = playlist_NodeCreate( p_playlist,
00489 p_parent->pp_parents[0]->i_view,
00490 psz_newname, p_parent );
00491
00492 playlist_CopyParents( p_parent, p_node );
00493
00494 p_node->input.i_type = ITEM_TYPE_DIRECTORY;
00495
00496 if( ReadDir( p_playlist, psz_uri , MODE_EXPAND,
00497 pi_position, p_node ) != VLC_SUCCESS )
00498 {
00499 return VLC_EGENERIC;
00500 }
00501
00502
00503 free( psz_newname );
00504 }
00505 }
00506 else
00507 {
00508 playlist_item_t *p_item;
00509 char *psz_tmp1, *psz_tmp2, *psz_loc;
00510
00511 if( i_extensions > 0 )
00512 {
00513 char *psz_dot = strrchr( p_dir_content->d_name, '.' );
00514 if( psz_dot++ && *psz_dot )
00515 {
00516 int a;
00517 for( a = 0; a < i_extensions; a++ )
00518 {
00519 if( !strcmp( psz_dot, ppsz_extensions[a] ) )
00520 break;
00521 }
00522 if( a < i_extensions )
00523 {
00524 msg_Dbg( p_playlist, "Ignoring file %s", psz_uri );
00525 free( psz_uri );
00526 continue;
00527 }
00528 }
00529 }
00530
00531 psz_loc = FromLocale( psz_uri );
00532 psz_tmp1 = vlc_fix_readdir_charset( VLC_OBJECT(p_playlist),
00533 psz_loc );
00534 LocaleFree( psz_loc );
00535
00536 psz_loc = FromLocale( p_dir_content->d_name );
00537 psz_tmp2 = vlc_fix_readdir_charset( VLC_OBJECT(p_playlist),
00538 psz_loc );
00539 LocaleFree( psz_loc );
00540
00541 p_item = playlist_ItemNewWithType( VLC_OBJECT(p_playlist),
00542 psz_tmp1, psz_tmp2, ITEM_TYPE_VFILE );
00543 playlist_NodeAddItem( p_playlist,p_item,
00544 p_parent->pp_parents[0]->i_view,
00545 p_parent,
00546 PLAYLIST_APPEND, PLAYLIST_END );
00547
00548 playlist_CopyParents( p_parent, p_item );
00549 }
00550 }
00551 free( psz_uri );
00552 }
00553
00554 for( i = 0; i < i_extensions; i++ )
00555 {
00556 if( ppsz_extensions[i] )
00557 free( ppsz_extensions[i] );
00558 }
00559 if( ppsz_extensions ) free( ppsz_extensions );
00560 if( psz_ignore ) free( psz_ignore );
00561
00562 free( pp_dir_content );
00563 return VLC_SUCCESS;
00564 }