Jelajahi Sumber

introduce VLCNetworkServerBrowser protocol to browse the contents of servers
refactor VLCFTPServerListViewController to use VLCNetworkServerBrowser

Tobias Conradi 9 tahun lalu
induk
melakukan
b5bb2d36ba

+ 2 - 2
Sources/LocalNetworkConnectivity/ServerBrowsing/VLCFTPServerListViewController.h

@@ -11,9 +11,9 @@
  *****************************************************************************/
  *****************************************************************************/
 
 
 #import "VLCNetworkListViewController.h"
 #import "VLCNetworkListViewController.h"
+#import "VLCNetworkServerBrowser-Protocol.h"
 
 
 @interface VLCFTPServerListViewController : VLCNetworkListViewController
 @interface VLCFTPServerListViewController : VLCNetworkListViewController
 
 
-- (id)initWithFTPServer:(NSString *)serverAddress userName:(NSString *)username andPassword:(NSString *)password atPath:(NSString *)path;
-
+- (instancetype)initWithServerBrowser:(id<VLCNetworkServerBrowser>)browser;
 @end
 @end

+ 85 - 165
Sources/LocalNetworkConnectivity/ServerBrowsing/VLCFTPServerListViewController.m

@@ -21,34 +21,23 @@
 #import "VLCDownloadViewController.h"
 #import "VLCDownloadViewController.h"
 
 
 #import "WhiteRaccoon.h"
 #import "WhiteRaccoon.h"
+#import "VLCNetworkServerBrowserFTP.h"
 
 
-@interface VLCFTPServerListViewController () <WRRequestDelegate, VLCNetworkListCellDelegate, UITableViewDataSource, UITableViewDelegate, UIActionSheetDelegate>
-{
-    NSString *_ftpServerAddress;
-    NSString *_ftpServerUserName;
-    NSString *_ftpServerPassword;
-    NSString *_ftpServerPath;
-    WRRequestListDirectory *_FTPListDirRequest;
-
-    NSArray *_objectList;
-    NSMutableArray *_searchData;
-}
-
+@interface VLCFTPServerListViewController () <VLCNetworkServerBrowserDelegate,VLCNetworkListCellDelegate, UITableViewDataSource, UITableViewDelegate, UIActionSheetDelegate>
+@property (nonatomic) VLCNetworkServerBrowserFTP *serverBrowser;
+@property (nonatomic) NSByteCountFormatter *byteCounterFormatter;
+@property (nonatomic) NSArray<id<VLCNetworkServerBrowserItem>> *searchArray;
 @end
 @end
 
 
 @implementation VLCFTPServerListViewController
 @implementation VLCFTPServerListViewController
 
 
-- (id)initWithFTPServer:(NSString *)serverAddress userName:(NSString *)username andPassword:(NSString *)password atPath:(NSString *)path
+- (instancetype)initWithServerBrowser:(id<VLCNetworkServerBrowser>)browser
 {
 {
     self = [super init];
     self = [super init];
-
     if (self) {
     if (self) {
-        _ftpServerAddress = serverAddress;
-        _ftpServerUserName = username;
-        _ftpServerPassword = password;
-        _ftpServerPath = path;
+        _serverBrowser = browser;
+        browser.delegate = self;
     }
     }
-
     return self;
     return self;
 }
 }
 
 
@@ -56,102 +45,90 @@
 {
 {
     [super viewDidLoad];
     [super viewDidLoad];
 
 
-    if ([_ftpServerPath isEqualToString:@"/"])
-        self.title = _ftpServerAddress;
-    else
-        self.title = [_ftpServerPath lastPathComponent];
-    [self _listFTPDirectory];
+    self.title = self.serverBrowser.title;
+    [self update];
 }
 }
 
 
-#pragma mark - ftp specifics
+- (void)networkServerBrowserDidUpdate:(id<VLCNetworkServerBrowser>)networkBrowser {
+    [self.tableView reloadData];
+    [[VLCActivityManager defaultManager] networkActivityStopped];
 
 
+}
 
 
-- (void)_listFTPDirectory
-{
-    if (_FTPListDirRequest)
-        return;
+- (void)networkServerBrowser:(id<VLCNetworkServerBrowser>)networkBrowser requestDidFailWithError:(NSError *)error {
 
 
-    _FTPListDirRequest = [[WRRequestListDirectory alloc] init];
-    _FTPListDirRequest.delegate = self;
-    _FTPListDirRequest.hostname = _ftpServerAddress;
-    _FTPListDirRequest.username = _ftpServerUserName;
-    _FTPListDirRequest.password = _ftpServerPassword;
-    _FTPListDirRequest.path = _ftpServerPath;
-    _FTPListDirRequest.passive = YES;
+    VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"LOCAL_SERVER_CONNECTION_FAILED_TITLE", nil)
+                                                      message:NSLocalizedString(@"LOCAL_SERVER_CONNECTION_FAILED_MESSAGE", nil)
+                                                     delegate:self
+                                            cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
+                                            otherButtonTitles:nil];
+    [alert show];
+    
+}
 
 
+- (void)update
+{
+    [self.serverBrowser update];
     [[VLCActivityManager defaultManager] networkActivityStarted];
     [[VLCActivityManager defaultManager] networkActivityStarted];
-    [_FTPListDirRequest start];
 }
 }
 
 
-- (NSString *)_credentials
-{
-    NSString *cred;
+#pragma mark -
+- (NSByteCountFormatter *)byteCounterFormatter {
+    if (!_byteCounterFormatter) {
+        _byteCounterFormatter = [[NSByteCountFormatter alloc] init];
+    }
+    return _byteCounterFormatter;
+}
 
 
-    if (_ftpServerUserName.length > 0) {
-        if (_ftpServerPassword.length > 0)
-            cred = [NSString stringWithFormat:@"%@:%@@", _ftpServerUserName, _ftpServerPassword];
-        else
-            cred = [NSString stringWithFormat:@"%@@", _ftpServerPassword];
-    } else
-        cred = @"";
+#pragma mark - ftp specifics
 
 
-    return [cred stringByStandardizingPath];
-}
 
 
-- (void)_downloadFTPFile:(NSString *)fileName
+- (void)_downloadFTPFile:(id<VLCNetworkServerBrowserItem>)item
 {
 {
-    NSURL *URLToQueue = [NSURL URLWithString:[[@"ftp" stringByAppendingFormat:@"://%@%@/%@/%@", [self _credentials], _ftpServerAddress, _ftpServerPath, fileName] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
-
-    [[VLCDownloadViewController sharedInstance] addURLToDownloadList:URLToQueue fileNameOfMedia:nil];
+    [[VLCDownloadViewController sharedInstance] addURLToDownloadList:item.URL fileNameOfMedia:nil];
 }
 }
 
 
-- (void)_streamFTPFile:(NSString *)fileName
+- (void)_streamFTPFile:(id<VLCNetworkServerBrowserItem>)item
 {
 {
     NSString *URLofSubtitle = nil;
     NSString *URLofSubtitle = nil;
-    NSArray *SubtitlesList = [self _searchSubtitle:fileName];
+    NSArray *SubtitlesList = [self _searchSubtitle:item.URL.lastPathComponent];
 
 
     if(SubtitlesList.count > 0)
     if(SubtitlesList.count > 0)
         URLofSubtitle = [self _getFileSubtitleFromFtpServer:SubtitlesList[0]];
         URLofSubtitle = [self _getFileSubtitleFromFtpServer:SubtitlesList[0]];
 
 
-    NSURL *URLToPlay = [NSURL URLWithString:[[@"ftp" stringByAppendingFormat:@"://%@%@/%@/%@", [self _credentials], _ftpServerAddress, _ftpServerPath, fileName] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+    NSURL *URLToPlay = item.URL;
 
 
     VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
     VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
     [vpc playURL:URLToPlay subtitlesFilePath:URLofSubtitle];
     [vpc playURL:URLToPlay subtitlesFilePath:URLofSubtitle];
 }
 }
 
 
-- (NSArray *)_searchSubtitle:(NSString *)url
+- (NSArray<NSURL*> *)_searchSubtitle:(NSString *)url
 {
 {
     NSString *urlTemp = [[url lastPathComponent] stringByDeletingPathExtension];
     NSString *urlTemp = [[url lastPathComponent] stringByDeletingPathExtension];
-    NSMutableArray *ObjList = [[NSMutableArray alloc] init];
-    NSUInteger count = _objectList.count;
 
 
-    for (NSUInteger i = 0; i < count; i++)
-        [ObjList addObject:[_objectList[i] objectForKey:(id)kCFFTPResourceName]];
+    NSMutableArray<NSURL*> *urls = [NSMutableArray arrayWithArray:[self.serverBrowser.items valueForKey:@"URL"]];
 
 
-    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@", urlTemp];
-    NSArray *results = [ObjList filteredArrayUsingPredicate:predicate];
+    NSPredicate *namePredicate = [NSPredicate predicateWithFormat:@"SELF.path contains[c] %@", urlTemp];
+    [urls filterUsingPredicate:namePredicate];
 
 
-    [ObjList removeAllObjects];
+    NSPredicate *formatPrediate = [NSPredicate predicateWithBlock:^BOOL(NSURL *_Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
+        return [evaluatedObject.path isSupportedSubtitleFormat];
+    }];
+    [urls filterUsingPredicate:formatPrediate];
 
 
-    count = results.count;
-
-    for (NSUInteger i = 0; i < count; i++) {
-        if ([results[i] isSupportedSubtitleFormat])
-            [ObjList addObject:results[i]];
-    }
-    return [NSArray arrayWithArray:ObjList];
+    return [NSArray arrayWithArray:urls];
 }
 }
 
 
-- (NSString *)_getFileSubtitleFromFtpServer:(NSString *)fileName
+- (NSString *)_getFileSubtitleFromFtpServer:(NSURL *)subtitleURL
 {
 {
-    NSURL *url = [NSURL URLWithString:[[@"ftp" stringByAppendingFormat:@"://%@%@/%@/%@", [self _credentials], _ftpServerAddress, _ftpServerPath, fileName] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+
     NSString *FileSubtitlePath = nil;
     NSString *FileSubtitlePath = nil;
-    NSData *receivedSub = [NSData dataWithContentsOfURL:url];
+    NSData *receivedSub = [NSData dataWithContentsOfURL:subtitleURL]; // TODO: fix synchronous load
 
 
     if (receivedSub.length < [[UIDevice currentDevice] freeDiskspace].longLongValue) {
     if (receivedSub.length < [[UIDevice currentDevice] freeDiskspace].longLongValue) {
         NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
         NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
         NSString *directoryPath = searchPaths[0];
         NSString *directoryPath = searchPaths[0];
-        FileSubtitlePath = [directoryPath stringByAppendingPathComponent:[fileName lastPathComponent]];
+        FileSubtitlePath = [directoryPath stringByAppendingPathComponent:[subtitleURL lastPathComponent]];
 
 
         NSFileManager *fileManager = [NSFileManager defaultManager];
         NSFileManager *fileManager = [NSFileManager defaultManager];
         if (![fileManager fileExistsAtPath:FileSubtitlePath]) {
         if (![fileManager fileExistsAtPath:FileSubtitlePath]) {
@@ -163,7 +140,7 @@
         [receivedSub writeToFile:FileSubtitlePath atomically:YES];
         [receivedSub writeToFile:FileSubtitlePath atomically:YES];
     } else {
     } else {
         VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
         VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
-                                                          message:[NSString stringWithFormat:NSLocalizedString(@"DISK_FULL_FORMAT", nil), [fileName lastPathComponent], [[UIDevice currentDevice] model]]
+                                                          message:[NSString stringWithFormat:NSLocalizedString(@"DISK_FULL_FORMAT", nil), [subtitleURL lastPathComponent], [[UIDevice currentDevice] model]]
                                                          delegate:self
                                                          delegate:self
                                                 cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
                                                 cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
                                                 otherButtonTitles:nil];
                                                 otherButtonTitles:nil];
@@ -178,9 +155,9 @@
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {
 {
     if (tableView == self.searchDisplayController.searchResultsTableView)
     if (tableView == self.searchDisplayController.searchResultsTableView)
-        return _searchData.count;
+        return _searchArray.count;
 
 
-    return _objectList.count;
+    return self.serverBrowser.items.count;
 }
 }
 
 
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
@@ -189,23 +166,23 @@
     if (cell == nil)
     if (cell == nil)
         cell = [VLCNetworkListCell cellWithReuseIdentifier:VLCNetworkListCellIdentifier];
         cell = [VLCNetworkListCell cellWithReuseIdentifier:VLCNetworkListCellIdentifier];
 
 
-    NSDictionary *cellObject;
-    if (tableView == self.searchDisplayController.searchResultsTableView)
-        cellObject = _searchData[indexPath.row];
-    else
-        cellObject = _objectList[indexPath.row];
 
 
-    NSString *rawFileName = [cellObject objectForKey:(id)kCFFTPResourceName];
-    NSData *flippedData = [rawFileName dataUsingEncoding:[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingFTPTextEncoding] intValue] allowLossyConversion:YES];
-    cell.title = [[NSString alloc] initWithData:flippedData encoding:NSUTF8StringEncoding];
+    id<VLCNetworkServerBrowserItem> item;
+    if (tableView == self.searchDisplayController.searchResultsTableView) {
+        item = _searchArray[indexPath.row];
+    } else {
+        item = self.serverBrowser.items[indexPath.row];
+    }
 
 
-    if ([cellObject[(id)kCFFTPResourceType] intValue] == 4) {
+    cell.title = item.name;
+
+    if (item.isContainer) {
         cell.isDirectory = YES;
         cell.isDirectory = YES;
         cell.icon = [UIImage imageNamed:@"folder"];
         cell.icon = [UIImage imageNamed:@"folder"];
     } else {
     } else {
         cell.isDirectory = NO;
         cell.isDirectory = NO;
         cell.icon = [UIImage imageNamed:@"blank"];
         cell.icon = [UIImage imageNamed:@"blank"];
-        cell.subtitle = [NSString stringWithFormat:@"%0.2f MB", (float)([cellObject[(id)kCFFTPResourceSize] intValue] / 1e6)];
+        cell.subtitle = item.fileSizeBytes ? [self.byteCounterFormatter stringFromByteCount:item.fileSizeBytes.longLongValue] : nil;
         cell.isDownloadable = YES;
         cell.isDownloadable = YES;
         cell.delegate = self;
         cell.delegate = self;
     }
     }
@@ -226,22 +203,19 @@
 
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 {
 {
-    NSDictionary *cellObject;
-
-    if (tableView == self.searchDisplayController.searchResultsTableView)
-        cellObject = _searchData[indexPath.row];
-    else
-        cellObject = _objectList[indexPath.row];
+    id<VLCNetworkServerBrowserItem> item;
+    if (tableView == self.searchDisplayController.searchResultsTableView) {
+        item = _searchArray[indexPath.row];
+    } else {
+        item = self.serverBrowser.items[indexPath.row];
+    }
 
 
-    if ([cellObject[(id)kCFFTPResourceType] intValue] == 4) {
-        NSString *newPath = [NSString stringWithFormat:@"%@/%@", _ftpServerPath, cellObject[(id)kCFFTPResourceName]];
+    if (item.isContainer) {
 
 
-        VLCFTPServerListViewController *targetViewController = [[VLCFTPServerListViewController alloc] initWithFTPServer:_ftpServerAddress userName:_ftpServerUserName andPassword:_ftpServerPassword atPath:newPath];
+        VLCFTPServerListViewController *targetViewController = [[VLCFTPServerListViewController alloc] initWithServerBrowser:item.containerBrowser];
         [self.navigationController pushViewController:targetViewController animated:YES];
         [self.navigationController pushViewController:targetViewController animated:YES];
     } else {
     } else {
-        NSString *rawObjectName = cellObject[(id)kCFFTPResourceName];
-        NSData *flippedData = [rawObjectName dataUsingEncoding:[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingFTPTextEncoding] intValue] allowLossyConversion:YES];
-        NSString *properObjectName = [[NSString alloc] initWithData:flippedData encoding:NSUTF8StringEncoding];
+        NSString *properObjectName = item.name;
         if (![properObjectName isSupportedFormat]) {
         if (![properObjectName isSupportedFormat]) {
             VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FILE_NOT_SUPPORTED", nil)
             VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FILE_NOT_SUPPORTED", nil)
                                                               message:[NSString stringWithFormat:NSLocalizedString(@"FILE_NOT_SUPPORTED_LONG", nil), properObjectName]
                                                               message:[NSString stringWithFormat:NSLocalizedString(@"FILE_NOT_SUPPORTED_LONG", nil), properObjectName]
@@ -250,7 +224,7 @@
                                                     otherButtonTitles:nil];
                                                     otherButtonTitles:nil];
             [alert show];
             [alert show];
         } else
         } else
-            [self _streamFTPFile:properObjectName];
+            [self _streamFTPFile:item];
     }
     }
     [tableView deselectRowAtIndexPath:indexPath animated:NO];
     [tableView deselectRowAtIndexPath:indexPath animated:NO];
 }
 }
@@ -258,35 +232,27 @@
 #pragma mark - VLCNetworkListCell delegation
 #pragma mark - VLCNetworkListCell delegation
 - (void)triggerDownloadForCell:(VLCNetworkListCell *)cell
 - (void)triggerDownloadForCell:(VLCNetworkListCell *)cell
 {
 {
-    NSString *rawObjectName;
-    NSInteger size;
-
-    NSDictionary *cellObject;
-
+    id<VLCNetworkServerBrowserItem> item;
     if ([self.searchDisplayController isActive])
     if ([self.searchDisplayController isActive])
-        cellObject = _searchData[[self.searchDisplayController.searchResultsTableView indexPathForCell:cell].row];
+        item = _searchArray[[self.searchDisplayController.searchResultsTableView indexPathForCell:cell].row];
     else
     else
-        cellObject = _objectList[[self.tableView indexPathForCell:cell].row];
+        item = self.serverBrowser.items[[self.tableView indexPathForCell:cell].row];
 
 
-    rawObjectName = cellObject[(id)kCFFTPResourceName];
-    size = [cellObject[(id)kCFFTPResourceSize] intValue];
 
 
-    NSData *flippedData = [rawObjectName dataUsingEncoding:[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingFTPTextEncoding] intValue] allowLossyConversion:YES];
-    NSString *properObjectName = [[NSString alloc] initWithData:flippedData encoding:NSUTF8StringEncoding];
-    if (![properObjectName isSupportedFormat]) {
+    if (![item.name isSupportedFormat]) {
         VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FILE_NOT_SUPPORTED", nil)
         VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FILE_NOT_SUPPORTED", nil)
-                                                          message:[NSString stringWithFormat:NSLocalizedString(@"FILE_NOT_SUPPORTED_LONG", nil), properObjectName]
+                                                          message:[NSString stringWithFormat:NSLocalizedString(@"FILE_NOT_SUPPORTED_LONG", nil), item.name]
                                                          delegate:self
                                                          delegate:self
                                                 cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
                                                 cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
                                                 otherButtonTitles:nil];
                                                 otherButtonTitles:nil];
         [alert show];
         [alert show];
     } else {
     } else {
-        if (size  < [[UIDevice currentDevice] freeDiskspace].longLongValue) {
-            [self _downloadFTPFile:properObjectName];
+        if (item.fileSizeBytes.longLongValue  < [[UIDevice currentDevice] freeDiskspace].longLongValue) {
+            [self _downloadFTPFile:item];
             [cell.statusLabel showStatusMessage:NSLocalizedString(@"DOWNLOADING", nil)];
             [cell.statusLabel showStatusMessage:NSLocalizedString(@"DOWNLOADING", nil)];
         } else {
         } else {
             VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
             VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
-                                                              message:[NSString stringWithFormat:NSLocalizedString(@"DISK_FULL_FORMAT", nil), properObjectName, [[UIDevice currentDevice] model]]
+                                                              message:[NSString stringWithFormat:NSLocalizedString(@"DISK_FULL_FORMAT", nil), item.name, [[UIDevice currentDevice] model]]
                                                              delegate:self
                                                              delegate:self
                                                     cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
                                                     cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
                                                     otherButtonTitles:nil];
                                                     otherButtonTitles:nil];
@@ -295,59 +261,13 @@
     }
     }
 }
 }
 
 
-#pragma mark - white raccoon delegation
-
-- (void)requestCompleted:(WRRequest *)request
-{
-    if (request == _FTPListDirRequest) {
-        NSMutableArray *filteredList = [[NSMutableArray alloc] init];
-        NSArray *rawList = [(WRRequestListDirectory*)request filesInfo];
-        NSUInteger count = rawList.count;
-
-        for (NSUInteger x = 0; x < count; x++) {
-            if (![[rawList[x] objectForKey:(id)kCFFTPResourceName] hasPrefix:@"."])
-                [filteredList addObject:rawList[x]];
-        }
-
-        _objectList = [NSArray arrayWithArray:filteredList];
-        [self.tableView reloadData];
-    } else
-        APLog(@"unknown request %@ completed", request);
-}
-
-- (void)requestFailed:(WRRequest *)request
-{
-    VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"LOCAL_SERVER_CONNECTION_FAILED_TITLE", nil)
-                                                      message:NSLocalizedString(@"LOCAL_SERVER_CONNECTION_FAILED_MESSAGE", nil)
-                                                     delegate:self
-                                            cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
-                                            otherButtonTitles:nil];
-    [alert show];
-
-    APLog(@"request %@ failed with error %i", request, request.error.errorCode);
-}
 
 
 #pragma mark - search
 #pragma mark - search
 
 
 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
 {
 {
-    NSInteger listCount = 0;
-    [_searchData removeAllObjects];
-
-    listCount = _objectList.count;
-
-    for (int i = 0; i < listCount; i++) {
-        NSRange nameRange;
-
-        NSString *rawObjectName = [_objectList[i] objectForKey:(id)kCFFTPResourceName];
-        NSData *flippedData = [rawObjectName dataUsingEncoding:[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingFTPTextEncoding] intValue] allowLossyConversion:YES];
-        NSString *properObjectName = [[NSString alloc] initWithData:flippedData encoding:NSUTF8StringEncoding];
-        nameRange = [properObjectName rangeOfString:searchString options:NSCaseInsensitiveSearch];
-
-        if (nameRange.location != NSNotFound)
-            [_searchData addObject:_objectList[i]];
-    }
-
+    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[c] %@",searchString];
+    self.searchArray = [self.serverBrowser.items filteredArrayUsingPredicate:predicate];
     return YES;
     return YES;
 }
 }
 
 

+ 9 - 1
Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkListViewController.m

@@ -76,7 +76,6 @@ NSString *VLCNetworkListCellIdentifier = @"VLCNetworkListCellIdentifier";
 
 
     _tapTwiceGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(tapTwiceGestureAction:)];
     _tapTwiceGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(tapTwiceGestureAction:)];
     [_tapTwiceGestureRecognizer setNumberOfTapsRequired:2];
     [_tapTwiceGestureRecognizer setNumberOfTapsRequired:2];
-    [self.navigationController.navigationBar addGestureRecognizer:_tapTwiceGestureRecognizer];
 
 
     self.navigationItem.rightBarButtonItem = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(menuButtonAction:)];
     self.navigationItem.rightBarButtonItem = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(menuButtonAction:)];
 
 
@@ -84,6 +83,15 @@ NSString *VLCNetworkListCellIdentifier = @"VLCNetworkListCellIdentifier";
     [_searchData removeAllObjects];
     [_searchData removeAllObjects];
 }
 }
 
 
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [self.navigationController.navigationBar addGestureRecognizer:_tapTwiceGestureRecognizer];
+}
+- (void)viewWillDisappear:(BOOL)animated {
+    [super viewDidDisappear:animated];
+    [self.navigationController.navigationBar removeGestureRecognizer:_tapTwiceGestureRecognizer];
+}
+
 - (BOOL)shouldAutorotate
 - (BOOL)shouldAutorotate
 {
 {
     UIInterfaceOrientation toInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
     UIInterfaceOrientation toInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];

+ 48 - 0
Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkServerBrowser-Protocol.h

@@ -0,0 +1,48 @@
+/*****************************************************************************
+ * VLCNetworkServerBrowser-Protocol.h
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Tobias Conradi <videolan # tobias-conradi.de>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol VLCNetworkServerBrowserItem;
+@protocol VLCNetworkServerBrowserDelegate;
+
+@protocol VLCNetworkServerBrowser <NSObject>
+
+@property (nonatomic, weak) id <VLCNetworkServerBrowserDelegate> delegate;
+@property (nonatomic, readonly, nullable) NSString *title;
+@property (nonatomic, readonly) NSArray<id<VLCNetworkServerBrowserItem>> *items;
+
+- (void)update;
+
+@end
+
+@protocol VLCNetworkServerBrowserDelegate <NSObject>
+- (void) networkServerBrowserDidUpdate:(id<VLCNetworkServerBrowser>)networkBrowser;
+- (void) networkServerBrowser:(id<VLCNetworkServerBrowser>)networkBrowser requestDidFailWithError:(NSError *)error;
+@end
+
+
+@protocol VLCNetworkServerBrowserItem <NSObject>
+@property (nonatomic, readonly, getter=isContainer) BOOL container;
+// if item is container browser is the browser for the container
+@property (nonatomic, readonly, nullable) id<VLCNetworkServerBrowser> containerBrowser;
+
+@property (nonatomic, readonly) NSString *name;
+@property (nonatomic, readonly) NSURL *URL;
+@property (nonatomic, readonly, nullable) NSNumber *fileSizeBytes;
+
+@end
+
+
+NS_ASSUME_NONNULL_END

+ 27 - 0
Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkServerBrowserFTP.h

@@ -0,0 +1,27 @@
+/*****************************************************************************
+ * VLCNetworkServerBrowserFTP.h
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Tobias Conradi <videolan # tobias-conradi.de>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import "VLCNetworkServerBrowser-Protocol.h"
+
+NS_ASSUME_NONNULL_BEGIN
+@interface VLCNetworkServerBrowserFTP : NSObject <VLCNetworkServerBrowser>
+- (instancetype)initWithFTPServer:(NSString *)serverAddress userName:(nullable NSString *)username andPassword:(nullable NSString *)password atPath:(NSString *)path;
+- (instancetype)initWithURL:(NSURL *)url NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+@end
+
+@interface VLCNetworkServerBrowserItemFTP : NSObject <VLCNetworkServerBrowserItem>
+- (instancetype)initWithDictionary:(NSDictionary *)dict baseURL:(NSURL *)baseURL;
+@end
+
+NS_ASSUME_NONNULL_END
+

+ 129 - 0
Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkServerBrowserFTP.m

@@ -0,0 +1,129 @@
+/*****************************************************************************
+ * VLCNetworkServerBrowserFTP.m
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Tobias Conradi <videolan # tobias-conradi.de>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import "VLCNetworkServerBrowserFTP.h"
+#import "WhiteRaccoon.h"
+
+@interface VLCNetworkServerBrowserFTP () <WRRequestDelegate>
+@property (nonatomic) NSURL *url;
+@property (nonatomic) WRRequestListDirectory *FTPListDirRequest;
+@property (nonatomic, readwrite) NSArray<id<VLCNetworkServerBrowserItem>> *items;
+
+@end
+
+@implementation VLCNetworkServerBrowserFTP
+@synthesize delegate = _delegate, items = _items;
+
+#pragma mark - Protocol conformance
+- (NSString *)title {
+    if ([_url.path isEqualToString:@"/"])
+        return _url.host;
+    else
+        return [_url.path lastPathComponent];
+}
+
+- (void)update {
+    if (_FTPListDirRequest)
+        return;
+
+    _FTPListDirRequest = [[WRRequestListDirectory alloc] init];
+    _FTPListDirRequest.delegate = self;
+    _FTPListDirRequest.hostname = _url.host;
+    _FTPListDirRequest.username = _url.user;
+    _FTPListDirRequest.password = _url.password;
+    _FTPListDirRequest.path = _url.path;
+    _FTPListDirRequest.passive = YES;
+
+    [_FTPListDirRequest start];
+}
+
+#pragma mark -
+
+- (instancetype)initWithFTPServer:(NSString *)serverAddress userName:(NSString *)username andPassword:(NSString *)password atPath:(NSString *)path
+{
+    NSURLComponents *components = [[NSURLComponents alloc] init];
+    components.scheme = @"ftp";
+    components.host = serverAddress;
+    components.user = username.length ? username : @"anonymous";
+    components.password = password.length ? password : nil;
+    components.path = path;
+    return [self initWithURL:components.URL];
+}
+
+- (instancetype)initWithURL:(NSURL *)url
+{
+    self = [super init];
+    if (self) {
+        _url = url;
+    }
+    return self;
+}
+
+#pragma mark - white raccoon delegation
+
+- (void)requestCompleted:(WRRequest *)request
+{
+    if (request == _FTPListDirRequest) {
+        NSMutableArray *filteredList = [[NSMutableArray alloc] init];
+        NSArray *rawList = [(WRRequestListDirectory*)request filesInfo];
+        NSUInteger count = rawList.count;
+
+        for (NSUInteger x = 0; x < count; x++) {
+            NSDictionary *dict = rawList[x];
+            if (![[dict objectForKey:(id)kCFFTPResourceName] hasPrefix:@"."])
+                [filteredList addObject:[[VLCNetworkServerBrowserItemFTP alloc] initWithDictionary:dict baseURL:self.url]];
+        }
+        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+            self.items = [NSArray arrayWithArray:filteredList];
+            [self.delegate networkServerBrowserDidUpdate:self];
+        }];
+    } else
+        APLog(@"unknown request %@ completed", request);
+}
+
+- (void)requestFailed:(WRRequest *)request
+{
+    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+        NSDictionary *userInfo = nil;
+        if (request.error.message) {
+            userInfo = @{ NSLocalizedDescriptionKey : request.error.message };
+        }
+        NSError *error = [NSError errorWithDomain:@"org.videolan.whiteracoon" code:request.error.errorCode userInfo:userInfo];
+        [self.delegate networkServerBrowser:self requestDidFailWithError:error];
+        APLog(@"request %@ failed with error %i", request, request.error.errorCode);
+    }];
+}
+
+@end
+
+@implementation VLCNetworkServerBrowserItemFTP
+@synthesize name = _name, container = _container, fileSizeBytes = _fileSizeBytes, URL = _URL;
+
+- (instancetype)initWithDictionary:(NSDictionary *)dict baseURL:(NSURL *)baseURL
+{
+    self = [super init];
+    if (self) {
+        NSString *rawFileName = [dict objectForKey:(id)kCFFTPResourceName];
+        NSData *flippedData = [rawFileName dataUsingEncoding:[[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingFTPTextEncoding] intValue] allowLossyConversion:YES];
+        _name = [[NSString alloc] initWithData:flippedData encoding:NSUTF8StringEncoding];
+        _container = [dict[(id)kCFFTPResourceType] intValue] == 4;
+        _fileSizeBytes = dict[(id)kCFFTPResourceSize];
+        _URL = [baseURL URLByAppendingPathComponent:_name];
+    }
+    return self;
+}
+
+- (id<VLCNetworkServerBrowser>)containerBrowser {
+    return [[VLCNetworkServerBrowserFTP alloc] initWithURL:self.URL];
+}
+
+@end

+ 5 - 4
Sources/LocalNetworkConnectivity/VLCServerListViewController.m

@@ -25,6 +25,8 @@
 #import "VLCDiscoveryListViewController.h"
 #import "VLCDiscoveryListViewController.h"
 #import "VLCFTPServerListViewController.h"
 #import "VLCFTPServerListViewController.h"
 
 
+#import "VLCNetworkServerBrowserFTP.h"
+
 @interface VLCServerListViewController () <UITableViewDataSource, UITableViewDelegate, VLCLocalServerDiscoveryControllerDelegate>
 @interface VLCServerListViewController () <UITableViewDataSource, UITableViewDelegate, VLCLocalServerDiscoveryControllerDelegate>
 {
 {
     VLCLocalServerDiscoveryController *_discoveryController;
     VLCLocalServerDiscoveryController *_discoveryController;
@@ -212,10 +214,9 @@ confirmedWithUsername:(NSString *)username
     switch (protocol) {
     switch (protocol) {
         case VLCServerProtocolFTP:
         case VLCServerProtocolFTP:
         {
         {
-            VLCFTPServerListViewController *targetViewController = [[VLCFTPServerListViewController alloc]
-                                                                    initWithFTPServer:server
-                                                                    userName:username
-                                                                    andPassword:password atPath:@"/"];
+            VLCNetworkServerBrowserFTP *browser = [[VLCNetworkServerBrowserFTP alloc]initWithFTPServer:server
+                                                                                              userName:username andPassword:password atPath:@"/"];
+            VLCFTPServerListViewController *targetViewController = [[VLCFTPServerListViewController alloc] initWithServerBrowser:browser];
             [self.navigationController pushViewController:targetViewController animated:YES];
             [self.navigationController pushViewController:targetViewController animated:YES];
             break;
             break;
         }
         }

+ 8 - 0
VLC for iOS.xcodeproj/project.pbxproj

@@ -266,6 +266,7 @@
 		DDE3C8DD1BD246DD00C03B8B /* VLCLocalNetworkServiceBrowserMediaDiscoverer.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8DC1BD246DD00C03B8B /* VLCLocalNetworkServiceBrowserMediaDiscoverer.m */; };
 		DDE3C8DD1BD246DD00C03B8B /* VLCLocalNetworkServiceBrowserMediaDiscoverer.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8DC1BD246DD00C03B8B /* VLCLocalNetworkServiceBrowserMediaDiscoverer.m */; };
 		DDE3C8E01BD24FAC00C03B8B /* VLCLocalNetworkServiceBrowserManualConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8DF1BD24FAC00C03B8B /* VLCLocalNetworkServiceBrowserManualConnect.m */; };
 		DDE3C8E01BD24FAC00C03B8B /* VLCLocalNetworkServiceBrowserManualConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8DF1BD24FAC00C03B8B /* VLCLocalNetworkServiceBrowserManualConnect.m */; };
 		DDE3C8E31BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8E21BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.m */; };
 		DDE3C8E31BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8E21BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.m */; };
+		DDE3C8E81BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8E71BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.m */; };
 		E0C04F951A25B4410080331A /* VLCDocumentPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C04F941A25B4410080331A /* VLCDocumentPickerController.m */; };
 		E0C04F951A25B4410080331A /* VLCDocumentPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C04F941A25B4410080331A /* VLCDocumentPickerController.m */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
@@ -824,6 +825,9 @@
 		DDE3C8DF1BD24FAC00C03B8B /* VLCLocalNetworkServiceBrowserManualConnect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLocalNetworkServiceBrowserManualConnect.m; sourceTree = "<group>"; };
 		DDE3C8DF1BD24FAC00C03B8B /* VLCLocalNetworkServiceBrowserManualConnect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLocalNetworkServiceBrowserManualConnect.m; sourceTree = "<group>"; };
 		DDE3C8E11BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCLocalNetworkServiceBrowserUPnP.h; sourceTree = "<group>"; };
 		DDE3C8E11BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCLocalNetworkServiceBrowserUPnP.h; sourceTree = "<group>"; };
 		DDE3C8E21BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLocalNetworkServiceBrowserUPnP.m; sourceTree = "<group>"; };
 		DDE3C8E21BD2592300C03B8B /* VLCLocalNetworkServiceBrowserUPnP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLocalNetworkServiceBrowserUPnP.m; sourceTree = "<group>"; };
+		DDE3C8E51BD2F75100C03B8B /* VLCNetworkServerBrowser-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "VLCNetworkServerBrowser-Protocol.h"; path = "Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkServerBrowser-Protocol.h"; sourceTree = SOURCE_ROOT; };
+		DDE3C8E61BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCNetworkServerBrowserFTP.h; path = Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkServerBrowserFTP.h; sourceTree = SOURCE_ROOT; };
+		DDE3C8E71BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCNetworkServerBrowserFTP.m; path = Sources/LocalNetworkConnectivity/ServerBrowsing/VLCNetworkServerBrowserFTP.m; sourceTree = SOURCE_ROOT; };
 		DDF157B31ACB169B00AAFBC6 /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = System/Library/Frameworks/WatchKit.framework; sourceTree = SDKROOT; };
 		DDF157B31ACB169B00AAFBC6 /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = System/Library/Frameworks/WatchKit.framework; sourceTree = SDKROOT; };
 		E09EACF57CDD22ABAE66CDD0 /* Pods-vlc-ios.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-vlc-ios.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-vlc-ios/Pods-vlc-ios.distribution.xcconfig"; sourceTree = "<group>"; };
 		E09EACF57CDD22ABAE66CDD0 /* Pods-vlc-ios.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-vlc-ios.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-vlc-ios/Pods-vlc-ios.distribution.xcconfig"; sourceTree = "<group>"; };
 		E0C04F931A25B4410080331A /* VLCDocumentPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCDocumentPickerController.h; path = Sources/VLCDocumentPickerController.h; sourceTree = SOURCE_ROOT; };
 		E0C04F931A25B4410080331A /* VLCDocumentPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCDocumentPickerController.h; path = Sources/VLCDocumentPickerController.h; sourceTree = SOURCE_ROOT; };
@@ -1501,6 +1505,9 @@
 				7DAE0C331B2EE0C200C53996 /* VLCFTPServerListViewController.m */,
 				7DAE0C331B2EE0C200C53996 /* VLCFTPServerListViewController.m */,
 				7DAE0C381B2F085F00C53996 /* VLCDiscoveryListViewController.h */,
 				7DAE0C381B2F085F00C53996 /* VLCDiscoveryListViewController.h */,
 				7DAE0C391B2F085F00C53996 /* VLCDiscoveryListViewController.m */,
 				7DAE0C391B2F085F00C53996 /* VLCDiscoveryListViewController.m */,
+				DDE3C8E51BD2F75100C03B8B /* VLCNetworkServerBrowser-Protocol.h */,
+				DDE3C8E61BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.h */,
+				DDE3C8E71BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.m */,
 			);
 			);
 			name = "Server browsing";
 			name = "Server browsing";
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2222,6 +2229,7 @@
 				7D168F7418D4A33F003FAF59 /* UIImage+Blur.m in Sources */,
 				7D168F7418D4A33F003FAF59 /* UIImage+Blur.m in Sources */,
 				9B5BEF2917FBAEA50016F9CB /* GTLDrive_Sources.m in Sources */,
 				9B5BEF2917FBAEA50016F9CB /* GTLDrive_Sources.m in Sources */,
 				7DC19ADF1868C7BB00810BF7 /* VLCFirstStepsViewController.m in Sources */,
 				7DC19ADF1868C7BB00810BF7 /* VLCFirstStepsViewController.m in Sources */,
+				DDE3C8E81BD3A81600C03B8B /* VLCNetworkServerBrowserFTP.m in Sources */,
 				7DE56C1A1AD93F9100E8CA00 /* VLCPlaybackController.m in Sources */,
 				7DE56C1A1AD93F9100E8CA00 /* VLCPlaybackController.m in Sources */,
 				7DC72D6317B7ED24008A26D0 /* WhiteRaccoon.m in Sources */,
 				7DC72D6317B7ED24008A26D0 /* WhiteRaccoon.m in Sources */,
 				DD7110F01AF38B2B00854776 /* MLMediaLibrary+playlist.m in Sources */,
 				DD7110F01AF38B2B00854776 /* MLMediaLibrary+playlist.m in Sources */,