Prechádzať zdrojové kódy

refactor VLCLocalServerDiscoveryController and VLCServerListView
reduce number of switches with magic numbers
add enum for LocalServerSections
introduce VLCLocalNetworkService as a wrapper around different network services

Tobias Conradi 9 rokov pred
rodič
commit
9b2b94d9f2

+ 75 - 0
Sources/VLCLocalNetworkService.h

@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * VLCLocalNetworkService.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 VLCLocalNetworkService <NSObject>
+
+@required
+@property (nonatomic, readonly, nullable) UIImage *icon;
+@property (nonatomic, readonly) NSString *title;
+
+@optional
+- (nullable UIViewController *)detailViewController;
+
+typedef void (^VLCLocalNetworkServiceActionBlock)(void);
+@property (nonatomic, readonly) VLCLocalNetworkServiceActionBlock action;
+@end
+
+#pragma mark - item
+
+@interface VLCLocalNetworkServiceItem : NSObject <VLCLocalNetworkService>
+- (instancetype)initWithTile:(NSString *)title icon:(nullable UIImage *)icon;
+@end
+
+@interface VLCLocalNetworkServiceItemLogin : VLCLocalNetworkServiceItem
+- (instancetype)init;
+@end
+
+
+#pragma mark - NetService based services
+@interface VLCLocalNetworkServiceNetService : NSObject <VLCLocalNetworkService>
+@property (nonatomic, readonly, strong) NSNetService *netService;
+- (instancetype)initWithNetService:(NSNetService *)service;
+@end
+
+@interface VLCLocalNetworkServicePlex : VLCLocalNetworkServiceNetService
+@end
+
+@interface VLCLocalNetworkServiceFTP : VLCLocalNetworkServiceNetService
+
+@end
+@interface VLCLocalNetworkServiceHTTP : VLCLocalNetworkServiceNetService
+@property (nonatomic, strong) NSDictionary *seviceDescription;
+
+@end
+
+#pragma mark - VLCMedia based services
+@interface VLCLocalNetworkServiceVLCMedia : NSObject <VLCLocalNetworkService>
+- (instancetype)initWithMediaItem:(VLCMedia *)mediaItem;
+@end
+
+@interface VLCLocalNetworkServiceDSM: VLCLocalNetworkServiceVLCMedia
+
+@end
+
+@interface VLCLocalNetworkServiceSAP: VLCLocalNetworkServiceVLCMedia
+
+@end
+
+#pragma mark - UPnP
+@class BasicUPnPDevice;
+@interface VLCLocalNetworkServiceUPnP : NSObject <VLCLocalNetworkService>
+- (instancetype)initWithUPnPDevice:(BasicUPnPDevice *)device;
+@end
+
+NS_ASSUME_NONNULL_END

+ 238 - 0
Sources/VLCLocalNetworkService.m

@@ -0,0 +1,238 @@
+/*****************************************************************************
+ * VLCLocalNetworkService.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 "VLCLocalNetworkService.h"
+
+
+@interface VLCLocalNetworkServiceItem ()
+@property (nonatomic, strong) NSString *title;
+@property (nonatomic, strong, nullable) UIImage *icon;
+@end
+
+@implementation VLCLocalNetworkServiceItem
+- (instancetype)initWithTile:(NSString *)title icon:(UIImage *)icon
+{
+    self = [super init];
+    if (self) {
+        _title = title;
+        _icon = icon;
+    }
+    return self;
+}
+@end
+
+#import "VLCNetworkLoginViewController.h"
+
+@implementation VLCLocalNetworkServiceItemLogin
+
+- (instancetype)init
+{
+    self = [super initWithTile:NSLocalizedString(@"CONNECT_TO_SERVER", nil)
+                          icon:[UIImage imageNamed:@"menuCone"]];
+    if (self) {
+
+    }
+    return self;
+}
+
+- (UIViewController *)detailViewController {
+    VLCNetworkLoginViewController *loginViewController = [[VLCNetworkLoginViewController alloc] initWithNibName:@"VLCNetworkLoginViewController" bundle:nil];
+    loginViewController.serverProtocol = VLCServerProtocolUndefined;
+    return loginViewController;
+}
+
+@end
+
+
+#pragma mark - NetService based services
+@interface VLCLocalNetworkServiceNetService()
+@property (nonatomic, strong) NSNetService *netService;
+@end
+@implementation VLCLocalNetworkServiceNetService
+
+- (instancetype)initWithNetService:(NSNetService *)service
+{
+    self = [super init];
+    if (self) {
+        _netService = service;
+    }
+    return self;
+}
+
+- (NSString *)title {
+    return self.netService.name;
+}
+- (UIImage *)icon {
+    return nil;
+}
+- (UIViewController *)detailViewController {
+    return nil;
+}
+@end
+
+#import "VLCLocalPlexFolderListViewController.h"
+@implementation VLCLocalNetworkServicePlex
+- (UIImage *)icon {
+    return [UIImage imageNamed:@"PlexServerIcon"];
+}
+- (UIViewController *)detailViewController {
+
+    NSNetService *service = self.netService;
+    if (service.hostName == nil || service.port == 0) {
+        return nil;
+    }
+
+    NSString *name = service.name;
+    NSString *hostName = service.hostName;
+    NSString *portNum = [[NSString alloc] initWithFormat:@":%ld", (long)service.port];
+    VLCLocalPlexFolderListViewController *targetViewController = [[VLCLocalPlexFolderListViewController alloc] initWithPlexServer:name serverAddress:hostName portNumber:portNum atPath:@"" authentification:@""];
+    return targetViewController;
+}
+@end
+
+@implementation VLCLocalNetworkServiceFTP
+- (UIImage *)icon {
+    return [UIImage imageNamed:@"serverIcon"];
+}
+- (UIViewController *)detailViewController {
+    VLCNetworkLoginViewController *loginViewController = [[VLCNetworkLoginViewController alloc] initWithNibName:@"VLCNetworkLoginViewController" bundle:nil];
+    loginViewController.serverProtocol = VLCServerProtocolFTP;
+    loginViewController.hostname = self.netService.hostName;
+    return loginViewController;
+}
+@end
+
+#import "VLCSharedLibraryListViewController.h"
+@implementation VLCLocalNetworkServiceHTTP
+- (UIImage *)icon {
+    return [UIImage imageNamed:@"menuCone"];
+}
+- (UIViewController *)detailViewController {
+
+    NSDictionary *serviceDescription = self.seviceDescription;
+    if (serviceDescription == nil) {
+        return nil;
+    }
+    NSString *name = serviceDescription[@"name"];
+    NSString *hostName = serviceDescription[@"hostName"];
+    NSString *portNum = serviceDescription[@"port"];
+    VLCSharedLibraryListViewController *targetViewController = [[VLCSharedLibraryListViewController alloc]
+                                                                initWithHttpServer:name
+                                                                serverAddress:hostName
+                                                                portNumber:portNum];
+    return targetViewController;
+}
+@end
+
+#pragma mark - VLCMedia based services
+@interface VLCLocalNetworkServiceVLCMedia()
+@property (nonatomic, strong) VLCMedia *mediaItem;
+@end
+@implementation VLCLocalNetworkServiceVLCMedia
+- (instancetype)initWithMediaItem:(VLCMedia *)mediaItem
+{
+    self = [super init];
+    if (self) {
+        _mediaItem = mediaItem;
+    }
+    return self;
+}
+- (NSString *)title {
+    return [self.mediaItem metadataForKey:VLCMetaInformationTitle];
+}
+- (UIImage *)icon {
+    return nil;
+}
+@end
+#import "VLCDiscoveryListViewController.h"
+
+@implementation VLCLocalNetworkServiceDSM
+- (UIImage *)icon {
+    return [UIImage imageNamed:@"serverIcon"];
+}
+- (UIViewController *)detailViewController {
+    VLCMedia *cellMedia = self.mediaItem;
+    if (cellMedia.mediaType != VLCMediaTypeDirectory)
+        return nil;
+
+    NSDictionary *mediaOptions = @{@"smb-user" : @"",
+                                   @"smb-pwd" : @"",
+                                   @"smb-domain" : @""};
+    [cellMedia addOptions:mediaOptions];
+
+    VLCDiscoveryListViewController *targetViewController = [[VLCDiscoveryListViewController alloc]
+                                                            initWithMedia:cellMedia
+                                                            options:mediaOptions];
+    return targetViewController;
+}
+@end
+#import "VLCPlaybackController.h"
+
+@implementation VLCLocalNetworkServiceSAP
+- (UIImage *)icon {
+    return [UIImage imageNamed:@"TVBroadcastIcon"];
+}
+- (VLCLocalNetworkServiceActionBlock)action {
+    __weak typeof(self) weakSelf = self;
+    return ^{
+        VLCMedia *cellMedia = weakSelf.mediaItem;
+
+        VLCMediaType mediaType = cellMedia.mediaType;
+        if (cellMedia && mediaType != VLCMediaTypeDirectory && mediaType != VLCMediaTypeDisc) {
+            VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+            [vpc playURL:[cellMedia url] successCallback:nil errorCallback:nil];
+        }
+    };
+}
+
+@end
+
+#pragma mark - UPnP
+#import "UPnPManager.h"
+#import "VLCUPnPServerListViewController.h"
+
+@interface VLCLocalNetworkServiceUPnP ()
+@property (nonatomic, strong) BasicUPnPDevice *device;
+@end
+
+@implementation VLCLocalNetworkServiceUPnP
+
+- (instancetype)initWithUPnPDevice:(BasicUPnPDevice *)device
+{
+    self = [super init];
+    if (self) {
+        _device = device;
+    }
+    return self;
+}
+
+- (NSString *)title {
+    return [self.device friendlyName];
+}
+- (UIImage *)icon {
+    return [self.device smallIcon] ?: [UIImage imageNamed:@"serverIcon"];
+}
+- (UIViewController *)detailViewController {
+
+    BasicUPnPDevice *device = self.device;
+    if (device != nil) {
+        if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"]) {
+            MediaServer1Device *server = (MediaServer1Device*)device;
+            VLCUPnPServerListViewController *targetViewController = [[VLCUPnPServerListViewController alloc] initWithUPNPDevice:server header:[device friendlyName] andRootID:@"0"];
+            return targetViewController;
+        }
+    }
+    return nil;
+}
+
+@end
+

+ 5 - 10
Sources/VLCLocalServerDiscoveryController.h

@@ -6,6 +6,7 @@
  * $Id$
  *
  * Authors: Felix Paul Kühne <fkuehne # videolan.org>
+ *          Tobias Conradi <videolan # tobias-conradi.de>
  *
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
@@ -13,6 +14,8 @@
 #import <Foundation/Foundation.h>
 #import "UPnPManager.h"
 
+#import "VLCLocalNetworkService.h"
+
 @protocol VLCLocalServerDiscoveryControllerDelegate <NSObject>
 
 @required
@@ -25,17 +28,9 @@
 @property (nonatomic, readwrite, weak) id delegate;
 @property (nonatomic, readonly) NSArray *sectionHeaderTexts;
 
-- (NSInteger)numberOfItemsInSection:(NSInteger)section;
-
-- (NSString *)titleForIndexPath:(NSIndexPath *)indexPath;
-- (UIImage *)iconForIndexPath:(NSIndexPath *)indexPath;
+- (id<VLCLocalNetworkService>)networkServiceForIndexPath:(NSIndexPath *)indexPath;
 
-- (BasicUPnPDevice *)upnpDeviceForIndexPath:(NSIndexPath *)indexPath;
-- (NSDictionary *)plexServiceDescriptionForIndexPath:(NSIndexPath *)indexPath;
-- (NSString *)ftpHostnameForIndexPath:(NSIndexPath *)indexPath;
-- (NSDictionary *)httpServiceDescriptionForIndexPath:(NSIndexPath *)indexPath;
-- (VLCMedia *)dsmDiscoveryForIndexPath:(NSIndexPath *)indexPath;
-- (VLCMedia *)sapDiscoveryForIndexPath:(NSIndexPath *)indexPath;
+- (NSInteger)numberOfItemsInSection:(NSInteger)section;
 
 - (void)stopDiscovery;
 - (BOOL)refreshDiscoveredData;

+ 110 - 164
Sources/VLCLocalServerDiscoveryController.m

@@ -7,6 +7,7 @@
  *
  * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  *          Pierre SAGASPE <pierre.sagaspe # me.com>
+ *          Tobias Conradi <videolan # tobias-conradi.de>
  *
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
@@ -33,19 +34,58 @@
 
 #define kPlexServiceType @"_plexmediasvr._tcp."
 
+typedef NS_ENUM(NSUInteger, VLCLocalServerSections) {
+    VLCLocalServerSectionGeneric = 0,
+    VLCLocalServerSectionUPnP,
+    VLCLocalServerSectionPlex,
+    VLCLocalServerSectionFTP,
+    VLCLocalServerSectionVLCiOS,
+    VLCLocalServerSectionSMB,
+    VLCLocalServerSectionSAP
+};
+
+@interface NSMutableArray(VLCLocalNetworkServiceNetService)
+-(NSUInteger)vlc_indexOfServiceWithNetService:(NSNetService*)netService;
+-(void)vlc_removeServiceWithNetService:(NSNetService*)netService;
+
+@end
+@implementation NSMutableArray (VLCLocalNetworkServiceNetService)
+
+- (NSUInteger)vlc_indexOfServiceWithNetService:(NSNetService *)netService {
+    NSUInteger index = [self indexOfObjectPassingTest:^BOOL(VLCLocalNetworkServiceNetService *obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        if (![obj respondsToSelector:@selector(netService)]) return false;
+
+        BOOL equal = [obj.netService isEqual:netService];
+        if (equal) {
+            *stop = YES;
+        }
+        return equal;
+    }];
+    return index;
+}
+
+-(void)vlc_removeServiceWithNetService:(NSNetService *)netService {
+    NSUInteger index = [self vlc_indexOfServiceWithNetService:netService];
+    if (index != NSNotFound) {
+        [self removeObjectAtIndex:index];
+    }
+}
+@end
+
+
 @interface VLCLocalServerDiscoveryController () <NSNetServiceBrowserDelegate, NSNetServiceDelegate, VLCMediaListDelegate, UPnPDBObserver>
 {
     NSNetServiceBrowser *_ftpNetServiceBrowser;
     NSNetServiceBrowser *_PlexNetServiceBrowser;
     NSNetServiceBrowser *_httpNetServiceBrowser;
-    NSMutableArray *_plexServices;
-    NSMutableArray *_PlexServicesInfo;
-    NSMutableArray *_httpServices;
-    NSMutableArray *_httpServicesInfo;
+    NSMutableArray<VLCLocalNetworkServicePlex*> *_plexServices;
+    NSMutableArray<VLCLocalNetworkServiceHTTP*> *_httpVLCServices;
+    NSMutableArray<VLCLocalNetworkServiceFTP*> *_ftpServices;
+
+    // to keep strong references while resolving
     NSMutableArray *_rawNetServices;
-    NSMutableArray *_ftpServices;
 
-    NSArray *_filteredUPNPDevices;
+    NSArray<VLCLocalNetworkServiceUPnP*> *_filteredUPNPDevices;
     NSArray *_UPNPdevices;
 
     VLCMediaDiscoverer *_sapDiscoverer;
@@ -98,7 +138,13 @@
 
 - (NSArray *)sectionHeaderTexts
 {
-    return @[@"Generic", @"Universal Plug'n'Play (UPnP)", @"Plex Media Server (via Bonjour)", @"File Transfer Protocol (FTP)", NSLocalizedString(@"SHARED_VLC_IOS_LIBRARY", nil), NSLocalizedString(@"SMB_CIFS_FILE_SERVERS", nil), @"SAP"];
+    return @[@"Generic",
+             @"Universal Plug'n'Play (UPnP)",
+             @"Plex Media Server (via Bonjour)",
+             @"File Transfer Protocol (FTP)",
+             NSLocalizedString(@"SHARED_VLC_IOS_LIBRARY", nil),
+             NSLocalizedString(@"SMB_CIFS_FILE_SERVERS", nil),
+             @"SAP"];
 }
 
 - (instancetype)init
@@ -132,12 +178,10 @@
     _ftpNetServiceBrowser.delegate = self;
 
     _plexServices = [[NSMutableArray alloc] init];
-    _PlexServicesInfo = [[NSMutableArray alloc] init];
     _PlexNetServiceBrowser = [[NSNetServiceBrowser alloc] init];
     _PlexNetServiceBrowser.delegate = self;
 
-    _httpServices = [[NSMutableArray alloc] init];
-    _httpServicesInfo = [[NSMutableArray alloc] init];
+    _httpVLCServices = [[NSMutableArray alloc] init];
     _httpNetServiceBrowser = [[NSNetServiceBrowser alloc] init];
     _httpNetServiceBrowser.delegate = self;
 
@@ -185,178 +229,87 @@
 
 #pragma mark - table view handling
 
-- (NSInteger)numberOfItemsInSection:(NSInteger)section
-{
-    switch (section) {
-        case 0:
-            return 1;
-
-        case 1:
-            return _filteredUPNPDevices.count;
-
-        case 2:
-            return _plexServices.count;
-
-        case 3:
-            return _ftpServices.count;
-
-        case 4:
-            return _httpServices.count;
-
-        case 5:
-            return _dsmDiscoverer.discoveredMedia.count;
-
-        case 6:
-            return _sapDiscoverer.discoveredMedia.count;
-
-        default:
-            return 0;
-    }
-}
-
-- (NSString *)titleForIndexPath:(NSIndexPath *)indexPath
+- (id<VLCLocalNetworkService>)networkServiceForIndexPath:(NSIndexPath *)indexPath
 {
+    VLCLocalServerSections section = indexPath.section;
     NSUInteger row = indexPath.row;
-    NSUInteger section = indexPath.section;
+
 
     switch (section) {
-        case 0:
+        case VLCLocalServerSectionGeneric:
         {
-            return NSLocalizedString(@"CONNECT_TO_SERVER", nil);
+            return [[VLCLocalNetworkServiceItemLogin alloc] init];
         }
 
-        case 1:
+        case VLCLocalServerSectionUPnP:
         {
             if (_filteredUPNPDevices.count > row) {
-                BasicUPnPDevice *device = _filteredUPNPDevices[row];
-                return [device friendlyName];
+                return _filteredUPNPDevices[row];
             }
-            return @"";
         }
 
-        case 2:
+        case VLCLocalServerSectionPlex:
         {
-            return [_plexServices[row] name];
+            return _plexServices[row];
         }
 
-        case 3:
+        case VLCLocalServerSectionFTP:
         {
-            return [_ftpServices[row] name];
+            return _ftpServices[row];
         }
 
-        case 4:
+        case VLCLocalServerSectionVLCiOS:
         {
-            return [_httpServices[row] name];
+            return _httpVLCServices[row];
         }
 
-        case 5:
+        case VLCLocalServerSectionSMB:
         {
-            return [[_dsmDiscoverer.discoveredMedia mediaAtIndex:row] metadataForKey: VLCMetaInformationTitle];
+            return [[VLCLocalNetworkServiceDSM alloc] initWithMediaItem:[_dsmDiscoverer.discoveredMedia mediaAtIndex:row]];
         }
 
-        case 6:
+        case VLCLocalServerSectionSAP:
         {
-            return [[_sapDiscoverer.discoveredMedia mediaAtIndex:row] metadataForKey: VLCMetaInformationTitle];
+            return [[VLCLocalNetworkServiceSAP alloc] initWithMediaItem:[_sapDiscoverer.discoveredMedia mediaAtIndex:row]];
         }
 
         default:
-            return @"";
+            break;
     }
+    return [[VLCLocalNetworkServiceItem alloc] initWithTile:@"FAIL"
+                                                       icon:nil];
+
 }
 
-- (UIImage *)iconForIndexPath:(NSIndexPath *)indexPath
+- (NSInteger)numberOfItemsInSection:(NSInteger)section
 {
-    NSUInteger row = indexPath.row;
-    NSUInteger section = indexPath.section;
-
     switch (section) {
-        case 0:
-        {
-            return [UIImage imageNamed:@"menuCone"];
-        }
+        case VLCLocalServerSectionGeneric:
+            return 1;
 
-        case 1:
-        {
-            UIImage *icon;
-            if (_filteredUPNPDevices.count > row) {
-                BasicUPnPDevice *device = _filteredUPNPDevices[row];
-                icon = [device smallIcon];
-            }
-            return icon != nil ? icon : [UIImage imageNamed:@"serverIcon"];
-        }
+        case VLCLocalServerSectionUPnP:
+            return _filteredUPNPDevices.count;
 
-        case 2:
-        {
-            return [UIImage imageNamed:@"PlexServerIcon"];
-        }
+        case VLCLocalServerSectionPlex:
+            return _plexServices.count;
 
-        case 3:
-        {
-            return [UIImage imageNamed:@"serverIcon"];
-        }
+        case VLCLocalServerSectionFTP:
+            return _ftpServices.count;
 
-        case 4:
-        {
-            return [UIImage imageNamed:@"menuCone"];
-        }
+        case VLCLocalServerSectionVLCiOS:
+            return _httpVLCServices.count;
 
-        case 5:
-        {
-            return [UIImage imageNamed:@"serverIcon"];
-        }
+        case VLCLocalServerSectionSMB:
+            return _dsmDiscoverer.discoveredMedia.count;
 
-        case 6:
-        {
-            return [UIImage imageNamed:@"TVBroadcastIcon"];
-        }
+        case VLCLocalServerSectionSAP:
+            return _sapDiscoverer.discoveredMedia.count;
 
         default:
-            return nil;
+            return 0;
     }
 }
 
-- (BasicUPnPDevice *)upnpDeviceForIndexPath:(NSIndexPath *)indexPath
-{
-    NSUInteger row = indexPath.row;
-    if (row > _filteredUPNPDevices.count || _filteredUPNPDevices.count == 0)
-        return nil;
-    return _filteredUPNPDevices[row];
-}
-
-- (NSDictionary *)plexServiceDescriptionForIndexPath:(NSIndexPath *)indexPath
-{
-    NSUInteger row = indexPath.row;
-    if (row > _PlexServicesInfo.count || _PlexServicesInfo.count == 0)
-        return nil;
-    return _PlexServicesInfo[row];
-}
-
-- (NSString *)ftpHostnameForIndexPath:(NSIndexPath *)indexPath
-{
-    NSUInteger row = indexPath.row;
-    if (row > _ftpServices.count || _ftpServices.count == 0)
-        return nil;
-    return [_ftpServices[row] hostName];
-}
-
-- (NSDictionary *)httpServiceDescriptionForIndexPath:(NSIndexPath *)indexPath
-{
-    NSUInteger row = indexPath.row;
-    if (row > _httpServicesInfo.count || _httpServicesInfo.count == 0)
-        return nil;
-    return _httpServicesInfo[row];
-}
-
-- (VLCMedia *)dsmDiscoveryForIndexPath:(NSIndexPath *)indexPath
-{
-    return [_dsmDiscoverer.discoveredMedia mediaAtIndex:indexPath.row];
-}
-
-- (VLCMedia *)sapDiscoveryForIndexPath:(NSIndexPath *)indexPath
-{
-    return [_sapDiscoverer.discoveredMedia mediaAtIndex:indexPath.row];
-}
-
 - (BOOL)refreshDiscoveredData
 {
     if (_reachability.currentReachabilityStatus != ReachableViaWiFi)
@@ -388,15 +341,17 @@
     APLog(@"bonjour service disappeared: %@ (%i)", aNetService.name, moreComing);
     if ([_rawNetServices containsObject:aNetService])
         [_rawNetServices removeObject:aNetService];
-    if ([aNetService.type isEqualToString:@"_ftp._tcp."])
-        [_ftpServices removeObject:aNetService];
+    if ([aNetService.type isEqualToString:@"_ftp._tcp."]) {
+        [_ftpServices vlc_removeServiceWithNetService:aNetService];
+    }
     if ([aNetService.type isEqualToString:kPlexServiceType]) {
-        [_plexServices removeObject:aNetService];
-        [_PlexServicesInfo removeAllObjects];
+        [_plexServices vlc_removeServiceWithNetService:aNetService];
     }
     if ([aNetService.type isEqualToString:@"_http._tcp."]) {
-        [_httpServices removeObject:aNetService];
-        [_httpServicesInfo removeAllObjects];
+        NSUInteger index = [_httpVLCServices vlc_indexOfServiceWithNetService:aNetService];
+        if (index != NSNotFound) {
+            [_httpVLCServices removeObjectAtIndex:index];
+        }
     }
     if (!moreComing) {
         if (self.delegate) {
@@ -410,17 +365,13 @@
 - (void)netServiceDidResolveAddress:(NSNetService *)aNetService
 {
     if ([aNetService.type isEqualToString:@"_ftp._tcp."]) {
-        if (![_ftpServices containsObject:aNetService])
-            [_ftpServices addObject:aNetService];
+        NSUInteger index = [_ftpServices vlc_indexOfServiceWithNetService:aNetService];
+        if (index == NSNotFound) {
+            [_ftpServices addObject:[[VLCLocalNetworkServiceFTP alloc] initWithNetService:aNetService]];
+        }
     } else if ([aNetService.type isEqualToString:kPlexServiceType]) {
-        if (![_plexServices containsObject:aNetService]) {
-            [_plexServices addObject:aNetService];
-            NSMutableDictionary *_dictService = [[NSMutableDictionary alloc] init];
-            [_dictService setObject:[aNetService name] forKey:@"name"];
-            [_dictService setObject:[aNetService hostName] forKey:@"hostName"];
-            NSString *portStr = [[NSString alloc] initWithFormat:@":%ld", (long)[aNetService port]];
-            [_dictService setObject:portStr forKey:@"port"];
-            [_PlexServicesInfo addObject:_dictService];
+        if ([_plexServices vlc_indexOfServiceWithNetService:aNetService] == NSNotFound) {
+            [_plexServices addObject:[[VLCLocalNetworkServicePlex alloc] initWithNetService: aNetService]];
         }
     }  else if ([aNetService.type isEqualToString:@"_http._tcp."]) {
         if ([[aNetService hostName] rangeOfString:_myHostName].location == NSNotFound) {
@@ -449,14 +400,9 @@
 {
     NSNetService *aNetService = [aNotification.userInfo objectForKey:@"aNetService"];
 
-    if (![_httpServices containsObject:aNetService]) {
-        [_httpServices addObject:aNetService];
-        NSMutableDictionary *_dictService = [[NSMutableDictionary alloc] init];
-        [_dictService setObject:[aNetService name] forKey:@"name"];
-        [_dictService setObject:[aNetService hostName] forKey:@"hostName"];
-        NSString *portStr = [[NSString alloc] initWithFormat:@"%ld", (long)[aNetService port]];
-        [_dictService setObject:portStr forKey:@"port"];
-        [_httpServicesInfo addObject:_dictService];
+    NSUInteger index = [_httpVLCServices vlc_indexOfServiceWithNetService:aNetService];
+    if (index == NSNotFound) {
+        [_httpVLCServices addObject:[[VLCLocalNetworkServiceHTTP alloc] initWithNetService:aNetService]];
     }
 }
 
@@ -520,11 +466,11 @@
 {
     NSUInteger count = _UPNPdevices.count;
     BasicUPnPDevice *device;
-    NSMutableArray *mutArray = [[NSMutableArray alloc] init];
+    NSMutableArray<VLCLocalNetworkServiceUPnP*> *mutArray = [[NSMutableArray alloc] init];
     for (NSUInteger x = 0; x < count; x++) {
         device = _UPNPdevices[x];
         if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"])
-            [mutArray addObject:device];
+            [mutArray addObject:[[VLCLocalNetworkServiceUPnP alloc] initWithUPnPDevice:device]];
         else
             APLog(@"found device '%@' with unsupported urn '%@'", [device friendlyName], [device urn]);
     }

+ 18 - 105
Sources/VLCServerListViewController.m

@@ -8,6 +8,7 @@
  * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  *          Pierre SAGASPE <pierre.sagaspe # me.com>
  *          Gleb Pinigin <gpinigin # gmail.com>
+ *          Tobias Conradi <videolan # tobias-conradi.de>
  *
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
@@ -139,10 +140,11 @@
     if (cell == nil)
         cell = [VLCNetworkListCell cellWithReuseIdentifier:CellIdentifier];
 
+    id<VLCLocalNetworkService> service = [_discoveryController networkServiceForIndexPath:indexPath];
 
     [cell setIsDirectory:YES];
-    [cell setIcon:[_discoveryController iconForIndexPath:indexPath]];
-    [cell setTitle:[_discoveryController titleForIndexPath:indexPath]];
+    [cell setIcon:service.icon];
+    [cell setTitle:service.title];
 
     return cell;
 }
@@ -151,65 +153,22 @@
 {
     [tableView deselectRowAtIndexPath:indexPath animated:YES];
 
-    NSUInteger section = indexPath.section;
+    id<VLCLocalNetworkService> service = [_discoveryController networkServiceForIndexPath:indexPath];
 
-    switch (section) {
-        case 0:
-        {
-            VLCNetworkLoginViewController *loginViewController = [[VLCNetworkLoginViewController alloc] initWithNibName:@"VLCNetworkLoginViewController" bundle:nil];
-            loginViewController.delegate = self;
-            loginViewController.serverProtocol = VLCServerProtocolUndefined;
-
-            UINavigationController *navCon = [[VLCNavigationController alloc] initWithRootViewController:loginViewController];
-            navCon.navigationBarHidden = NO;
-
-            if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
-                navCon.modalPresentationStyle = UIModalPresentationFormSheet;
-                [self presentViewController:navCon animated:YES completion:nil];
-
-                if (loginViewController.navigationItem.leftBarButtonItem == nil)
-                    loginViewController.navigationItem.leftBarButtonItem = [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_DONE", nil) target:loginViewController andSelector:@selector(_dismiss)];
-            } else
-                [self.navigationController pushViewController:loginViewController animated:YES];
-            break;
-        }
-        case 1:
-        {
-            [_activityIndicator startAnimating];
-            BasicUPnPDevice *device = [_discoveryController upnpDeviceForIndexPath:indexPath];
-            if (device != nil) {
-                if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"]) {
-                    MediaServer1Device *server = (MediaServer1Device*)device;
-                    VLCUPnPServerListViewController *targetViewController = [[VLCUPnPServerListViewController alloc] initWithUPNPDevice:server header:[device friendlyName] andRootID:@"0"];
-                    [self.navigationController pushViewController:targetViewController animated:YES];
-                }
-            }
-            break;
-        }
+    if ([service respondsToSelector:@selector(action)]) {
+        service.action();
+    }
 
-        case 2:
-        {
-            NSDictionary *serviceDescription = [_discoveryController plexServiceDescriptionForIndexPath:indexPath];
-            if (serviceDescription != nil) {
-                NSString *name = serviceDescription[@"name"];
-                NSString *hostName = serviceDescription[@"hostName"];
-                NSString *portNum = serviceDescription[@"port"];
-                VLCLocalPlexFolderListViewController *targetViewController = [[VLCLocalPlexFolderListViewController alloc] initWithPlexServer:name serverAddress:hostName portNumber:portNum atPath:@"" authentification:@""];
-                [[self navigationController] pushViewController:targetViewController animated:YES];
-            }
-            break;
-        }
+    if ([service respondsToSelector:@selector(detailViewController)]) {
+        UIViewController *controller = [service detailViewController];
 
-        case 3:
-        {
-            VLCNetworkLoginViewController *loginViewController = [[VLCNetworkLoginViewController alloc] initWithNibName:@"VLCNetworkLoginViewController" bundle:nil];
+        // TODO: refactor this out
+        if ([controller isKindOfClass:[VLCNetworkLoginViewController class]]) {
+            VLCNetworkLoginViewController *loginViewController = (id)controller;
             loginViewController.delegate = self;
-            loginViewController.serverProtocol = VLCServerProtocolFTP;
-
-            UINavigationController *navCon = [[VLCNavigationController alloc] initWithRootViewController:loginViewController];
-            navCon.navigationBarHidden = NO;
-
             if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+                UINavigationController *navCon = [[VLCNavigationController alloc] initWithRootViewController:loginViewController];
+                navCon.navigationBarHidden = NO;
                 navCon.modalPresentationStyle = UIModalPresentationFormSheet;
                 [self presentViewController:navCon animated:YES completion:nil];
 
@@ -217,57 +176,11 @@
                     loginViewController.navigationItem.leftBarButtonItem = [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_DONE", nil) target:loginViewController andSelector:@selector(_dismiss)];
             } else
                 [self.navigationController pushViewController:loginViewController animated:YES];
-            loginViewController.hostname = [_discoveryController ftpHostnameForIndexPath:indexPath];
-            break;
-        }
-
-        case 4:
-        {
-            NSDictionary *serviceDescription = [_discoveryController httpServiceDescriptionForIndexPath:indexPath];
-            if (serviceDescription != nil) {
-                NSString *name = serviceDescription[@"name"];
-                NSString *hostName = serviceDescription[@"hostName"];
-                NSString *portNum = serviceDescription[@"port"];
-                VLCSharedLibraryListViewController *targetViewController = [[VLCSharedLibraryListViewController alloc]
-                                                                            initWithHttpServer:name
-                                                                            serverAddress:hostName
-                                                                            portNumber:portNum];
-                [[self navigationController] pushViewController:targetViewController animated:YES];
-            }
-            break;
-        }
-
-        case 5:
-        {
-            VLCMedia *cellMedia = [_discoveryController dsmDiscoveryForIndexPath:indexPath];
-            if (cellMedia.mediaType != VLCMediaTypeDirectory)
-                return;
-
-            NSDictionary *mediaOptions = @{@"smb-user" : @"",
-                                           @"smb-pwd" : @"",
-                                           @"smb-domain" : @""};
-            [cellMedia addOptions:mediaOptions];
-
-            VLCDiscoveryListViewController *targetViewController = [[VLCDiscoveryListViewController alloc]
-                                                                    initWithMedia:cellMedia
-                                                                    options:mediaOptions];
-            [[self navigationController] pushViewController:targetViewController animated:YES];
-            break;
         }
-
-        case 6:
-        {
-            VLCMedia *cellMedia = [_discoveryController sapDiscoveryForIndexPath:indexPath];
-            VLCMediaType mediaType = cellMedia.mediaType;
-            if (mediaType != VLCMediaTypeDirectory && mediaType != VLCMediaTypeDisc) {
-                VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
-                [vpc playURL:[cellMedia url] successCallback:nil errorCallback:nil];
-            }
-            break;
+        // end TODO
+        else if (controller) {
+            [self.navigationController pushViewController:controller animated:YES];
         }
-
-        default:
-            break;
     }
 }
 

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

@@ -260,6 +260,7 @@
 		DDB7C6A41BAEB28200E6570E /* WKInterfaceController+VLCConnectionAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9FBE761BAD6BB600FFE77A /* WKInterfaceController+VLCConnectionAlert.m */; };
 		DDB959421AFBB30500BB8CFF /* MappingModel_2_5_to_2_6.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = DDB959411AFBB30500BB8CFF /* MappingModel_2_5_to_2_6.xcmappingmodel */; };
 		DDC10BE41AEE8EA700890DC3 /* VLCTimeNavigationTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = DDC10BE31AEE8EA700890DC3 /* VLCTimeNavigationTitleView.m */; };
+		DDE3C8D01BD0246500C03B8B /* VLCLocalNetworkService.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE3C8CF1BD0246500C03B8B /* VLCLocalNetworkService.m */; };
 		E0C04F951A25B4410080331A /* VLCDocumentPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C04F941A25B4410080331A /* VLCDocumentPickerController.m */; };
 /* End PBXBuildFile section */
 
@@ -804,6 +805,8 @@
 		DDC10BE21AEE8EA700890DC3 /* VLCTimeNavigationTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCTimeNavigationTitleView.h; path = Sources/VLCTimeNavigationTitleView.h; sourceTree = SOURCE_ROOT; };
 		DDC10BE31AEE8EA700890DC3 /* VLCTimeNavigationTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCTimeNavigationTitleView.m; path = Sources/VLCTimeNavigationTitleView.m; sourceTree = SOURCE_ROOT; };
 		DDE1BCE51B676B8800A4B9CE /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
+		DDE3C8CE1BD0246500C03B8B /* VLCLocalNetworkService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCLocalNetworkService.h; path = Sources/VLCLocalNetworkService.h; sourceTree = SOURCE_ROOT; };
+		DDE3C8CF1BD0246500C03B8B /* VLCLocalNetworkService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCLocalNetworkService.m; path = Sources/VLCLocalNetworkService.m; sourceTree = SOURCE_ROOT; };
 		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>"; };
 		E0C04F931A25B4410080331A /* VLCDocumentPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCDocumentPickerController.h; path = Sources/VLCDocumentPickerController.h; sourceTree = SOURCE_ROOT; };
@@ -1121,12 +1124,15 @@
 				7DBB78871B3028C400894467 /* Server browsing */,
 				7D30F3D5183AB2F100FFC021 /* VLCServerListViewController.h */,
 				7D30F3D6183AB2F100FFC021 /* VLCServerListViewController.m */,
+				DDE3C8CE1BD0246500C03B8B /* VLCLocalNetworkService.h */,
+				DDE3C8CF1BD0246500C03B8B /* VLCLocalNetworkService.m */,
 				7D37E3981BC93F7500AFA70E /* VLCLocalServerDiscoveryController.h */,
 				7D37E3991BC93F7500AFA70E /* VLCLocalServerDiscoveryController.m */,
 				7D30F3DA183AB2F900FFC021 /* VLCNetworkLoginViewController.h */,
 				7D30F3DB183AB2F900FFC021 /* VLCNetworkLoginViewController.m */,
 			);
 			name = "Local Network Connectivity";
+			path = Sources;
 			sourceTree = "<group>";
 		};
 		7D31CF061746AEF2005997E0 /* UI Elements */ = {
@@ -1798,6 +1804,7 @@
 				F2C5CAE560C074258A574B08 /* Copy Pods Resources */,
 				4173AEC21ABF1B860004101D /* Embed App Extensions */,
 				DD3567881B6760BF00338947 /* Embed Watch Content */,
+				EF695E012B3C222ED11192E3 /* Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -2069,6 +2076,21 @@
 			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
 			showEnvVarsInLog = 0;
 		};
+		EF695E012B3C222ED11192E3 /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-vlc-ios/Pods-vlc-ios-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		F2C5CAE560C074258A574B08 /* Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -2149,6 +2171,7 @@
 				7D168F7418D4A33F003FAF59 /* UIImage+Blur.m in Sources */,
 				9B5BEF2917FBAEA50016F9CB /* GTLDrive_Sources.m in Sources */,
 				7DC19ADF1868C7BB00810BF7 /* VLCFirstStepsViewController.m in Sources */,
+				DDE3C8D01BD0246500C03B8B /* VLCLocalNetworkService.m in Sources */,
 				7DE56C1A1AD93F9100E8CA00 /* VLCPlaybackController.m in Sources */,
 				7DC72D6317B7ED24008A26D0 /* WhiteRaccoon.m in Sources */,
 				DD7110F01AF38B2B00854776 /* MLMediaLibrary+playlist.m in Sources */,