Browse Source

plex: add connect manually to plex media server

Signed-off-by: Felix Paul Kühne <fkuehne@videolan.org>
Pierre SAGASPE 10 years ago
parent
commit
d77c309df4

+ 113 - 0
Resources/VLCFuturePlexConnectServerViewController.xib

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="VLCPlexConnectServerViewController">
+            <connections>
+                <outlet property="bookmarkButton" destination="axZ-sK-2Iz" id="2mV-Id-d8X"/>
+                <outlet property="bookmarkLabel" destination="zDw-Zb-GT1" id="x4w-tB-Moz"/>
+                <outlet property="connectButton" destination="6" id="ZVh-ZT-GxG"/>
+                <outlet property="portField" destination="44" id="hMY-NU-2k6"/>
+                <outlet property="portLabel" destination="Qzy-U8-tjZ" id="mLF-yD-pdj"/>
+                <outlet property="serverAddressField" destination="7" id="DLF-Dd-P4c"/>
+                <outlet property="serverAddressHelpLabel" destination="LCc-UZ-fl6" id="Zcc-X6-iZK"/>
+                <outlet property="serverAddressLabel" destination="Mql-Cg-dnG" id="4kM-9A-Hfj"/>
+                <outlet property="serverPlexBookmark" destination="105" id="tjj-ra-NML"/>
+                <outlet property="view" destination="1" id="xQw-Iz-yjA"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="1">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="478"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Server" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Mql-Cg-dnG">
+                    <rect key="frame" x="0.0" y="5" width="52" height="31"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <textField clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="yourserver.local" textAlignment="center" minimumFontSize="17" clearButtonMode="unlessEditing" id="7">
+                    <rect key="frame" x="58.000000054102664" y="5" width="260" height="31"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxY="YES"/>
+                    <color key="backgroundColor" red="0.28627450980392155" green="0.28627450980392155" blue="0.28627450980392155" alpha="1" colorSpace="calibratedRGB"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert"/>
+                </textField>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Port" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Qzy-U8-tjZ">
+                    <rect key="frame" x="0.0" y="39" width="52" height="31"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <textField clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="32400" textAlignment="center" minimumFontSize="17" clearButtonMode="unlessEditing" id="44">
+                    <rect key="frame" x="58.000000054102649" y="39" width="260" height="31"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxY="YES"/>
+                    <color key="backgroundColor" red="0.28627450980392155" green="0.28627450980392155" blue="0.28627450980392155" alpha="1" colorSpace="calibratedRGB"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert"/>
+                </textField>
+                <button contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="6">
+                    <rect key="frame" x="0.0" y="78" width="320" height="40"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                    <state key="normal" title="Connect"/>
+                    <connections>
+                        <action selector="connectToServer:" destination="-1" eventType="touchUpInside" id="isI-8i-6LQ"/>
+                    </connections>
+                </button>
+                <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsVerticalScrollIndicator="NO" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="105">
+                    <rect key="frame" x="0.0" y="256" width="320" height="222"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                    <color key="separatorColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+                    <connections>
+                        <outlet property="dataSource" destination="-1" id="1fS-Dx-iQ6"/>
+                        <outlet property="delegate" destination="-1" id="NMO-N4-N3u"/>
+                    </connections>
+                </tableView>
+                <label clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enter the IP or the name of the server you want to connect to." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="9" id="LCc-UZ-fl6">
+                    <rect key="frame" x="0.0" y="120" width="320" height="42"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                    <color key="backgroundColor" red="0.15686274510000001" green="0.15686274510000001" blue="0.15686274510000001" alpha="1" colorSpace="calibratedRGB"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" red="0.58823529411764708" green="0.58823529411764708" blue="0.58823529411764708" alpha="1" colorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="BookMark" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zDw-Zb-GT1">
+                    <rect key="frame" x="0.0" y="227" width="320" height="21"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <button contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="axZ-sK-2Iz">
+                    <rect key="frame" x="0.0" y="168" width="320" height="40"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                    <state key="normal" title="Save"/>
+                    <connections>
+                        <action selector="savePlexServer:" destination="-1" eventType="touchUpInside" id="wza-A5-qL6"/>
+                    </connections>
+                </button>
+            </subviews>
+            <color key="backgroundColor" red="0.15686274509803921" green="0.15686274509803921" blue="0.15686274509803921" alpha="1" colorSpace="calibratedRGB"/>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="47" y="379"/>
+        </view>
+    </objects>
+    <simulatedMetricsContainer key="defaultSimulatedMetrics">
+        <simulatedStatusBarMetrics key="statusBar"/>
+        <simulatedOrientationMetrics key="orientation"/>
+        <simulatedScreenMetrics key="destination" type="retina4"/>
+    </simulatedMetricsContainer>
+</document>

+ 120 - 0
Resources/VLCPlexConnectServerViewController.xib

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="VLCPlexConnectServerViewController">
+            <connections>
+                <outlet property="bookmarkButton" destination="110" id="xoG-FE-afx"/>
+                <outlet property="bookmarkLabel" destination="5nQ-rn-678" id="PZc-yc-CEu"/>
+                <outlet property="connectButton" destination="6" id="LBj-a5-yMn"/>
+                <outlet property="portField" destination="7gj-YW-2GC" id="uDa-Zz-r75"/>
+                <outlet property="serverAddressField" destination="7" id="8gp-Z1-DCd"/>
+                <outlet property="serverAddressHelpLabel" destination="5" id="kvX-RO-dmX"/>
+                <outlet property="serverPlexBookmark" destination="105" id="6Ra-zS-MmU"/>
+                <outlet property="view" destination="1" id="XFQ-Eh-Ce9"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="1">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="426"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <view contentMode="scaleToFill" id="4">
+                    <rect key="frame" x="0.0" y="0.0" width="320" height="154"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                    <subviews>
+                        <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="yourserver.local" minimumFontSize="17" background="input.png" clearButtonMode="unlessEditing" id="7">
+                            <rect key="frame" x="10" y="15" width="210" height="30"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <textInputTraits key="textInputTraits" autocorrectionType="no"/>
+                        </textField>
+                        <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="32400" minimumFontSize="17" background="input.png" clearButtonMode="unlessEditing" id="7gj-YW-2GC">
+                            <rect key="frame" x="10" y="53" width="210" height="30"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <textInputTraits key="textInputTraits" autocorrectionType="no"/>
+                        </textField>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="6" customClass="VLCMenuButton">
+                            <rect key="frame" x="228" y="29" width="82" height="39"/>
+                            <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                            <state key="normal" title="Connect" backgroundImage="menuButton.png">
+                                <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                            </state>
+                            <state key="highlighted">
+                                <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                            </state>
+                            <connections>
+                                <action selector="connectToServer:" destination="-1" eventType="touchUpInside" id="o0b-T6-5gP"/>
+                            </connections>
+                        </button>
+                        <label clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Enter the IP or the name of the server you want to connect to." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="9" id="5">
+                            <rect key="frame" x="10" y="96" width="300" height="50"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                            <color key="backgroundColor" red="0.1052877679" green="0.1052846164" blue="0.1052864045" alpha="1" colorSpace="calibratedRGB"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.74659199620000005" green="0.74659199620000005" blue="0.74659199620000005" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" red="0.1052877679" green="0.1052846164" blue="0.1052864045" alpha="1" colorSpace="calibratedRGB"/>
+                </view>
+                <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsVerticalScrollIndicator="NO" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="105">
+                    <rect key="frame" x="0.0" y="223" width="320" height="203"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                    <color key="separatorColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+                    <connections>
+                        <outlet property="dataSource" destination="-1" id="vCD-3Z-tl0"/>
+                        <outlet property="delegate" destination="-1" id="wun-MD-UD8"/>
+                    </connections>
+                </tableView>
+                <view contentMode="scaleToFill" id="32">
+                    <rect key="frame" x="0.0" y="162" width="320" height="53"/>
+                    <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+                    <subviews>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="110">
+                            <rect key="frame" x="234" y="10" width="73" height="33"/>
+                            <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                            <state key="normal" title="Save" backgroundImage="menuButton.png">
+                                <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                            </state>
+                            <state key="highlighted">
+                                <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                            </state>
+                            <connections>
+                                <action selector="savePlexServer:" destination="-1" eventType="touchUpInside" id="xeO-b0-b1Z"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Bookmark" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="5nQ-rn-678">
+                            <rect key="frame" x="8" y="18" width="210" height="21"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" red="0.10528776794672012" green="0.10528461635112762" blue="0.10528640449047089" alpha="1" colorSpace="calibratedRGB"/>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="input.png" width="199" height="29"/>
+        <image name="menuButton.png" width="63" height="39"/>
+    </resources>
+    <simulatedMetricsContainer key="defaultSimulatedMetrics">
+        <simulatedStatusBarMetrics key="statusBar"/>
+        <simulatedOrientationMetrics key="orientation"/>
+        <simulatedScreenMetrics key="destination" type="retina4"/>
+    </simulatedMetricsContainer>
+</document>

BIN
Resources/en.lproj/Localizable.strings


+ 5 - 0
Sources/VLCConstants.h

@@ -61,6 +61,11 @@
 #define kVLCLastFTPLogin @"last-ftp-login"
 #define kVLCLastFTPPassword @"last-ftp-pass"
 
+#define kVLCPLEXServer @"plex-server"
+#define kVLCPLEXPort @"plex-port"
+#define kVLCLastPLEXServer @"last-plex-server"
+#define kVLCLastPLEXPort @"last-plex-port"
+
 #define kSupportedFileExtensions @"\\.(3gp|3gp|3gp2|3gpp|amv|asf|avi|axv|divx|dv|flv|f4v|gvi|gxf|m1v|m2p|m2t|m2ts|m2v|m4v|mks|mkv|moov|mov|mp2v|mp4|mpeg|mpeg1|mpeg2|mpeg4|mpg|mpv|mt2s|mts|mxf|mxg|nsv|nuv|oga|ogg|ogm|ogv|ogx|spx|ps|qt|rec|rm|rmvb|tod|ts|tts|vob|vro|webm|wm|wmv|wtv|xesc)$"
 #define kSupportedSubtitleFileExtensions @"\\.(srt|sub|cdg|idx|utf|ass|ssa|aqt|jss|psb|rt|smi|txt|smil)$"
 #define kSupportedAudioFileExtensions @"\\.(aac|aiff|aif|amr|aob|ape|axa|caf|flac|it|m2a|m4a|m4b|mka|mlp|mod|mp1|mp2|mp3|mpa|mpc|mpga|oga|ogg|oma|opus|rmi|s3m|spx|tta|voc|vqf|wav|w64|wma|wv|xa|xm)$"

+ 29 - 8
Sources/VLCLocalServerListViewController.m

@@ -19,6 +19,7 @@
 #import "VLCLocalNetworkListCell.h"
 #import "VLCLocalServerFolderListViewController.h"
 #import "VLCLocalPlexFolderListViewController.h"
+#import "VLCPlexConnectServerViewController.h"
 #import "VLCSharedLibraryListViewController.h"
 #import "VLCSharedLibraryParser.h"
 #import <QuartzCore/QuartzCore.h>
@@ -294,7 +295,7 @@
     if (section == 0)
         return _filteredUPNPDevices.count;
     else if (section == 1)
-        return _PlexServices.count;
+        return (_PlexServices.count + 1);
     else if (section == 2)
         return _ftpServices.count;
     else if (section == 3)
@@ -334,8 +335,13 @@
         }
         [cell setIcon:icon != nil ? icon : [UIImage imageNamed:@"serverIcon"]];
     } else if (section == 1) {
-        [cell setTitle:[_PlexServices[row] name]];
-        [cell setIcon:[UIImage imageNamed:@"PlexServerIcon"]];
+        NSInteger totalRow = [tableView numberOfRowsInSection:section];
+        if (row == (totalRow - 1))
+            [cell setTitle:NSLocalizedString(@"PLEX_CONNECT_TO_SERVER", nil)];
+        else {
+            [cell setTitle:[_PlexServices[row] name]];
+            [cell setIcon:[UIImage imageNamed:@"PlexServerIcon"]];
+        }
     } else if (section == 2) {
         if (row == 0)
             [cell setTitle:_ftpServices[row]];
@@ -371,11 +377,26 @@
             [self.navigationController pushViewController:targetViewController animated:YES];
         }
     } else if (section == 1) {
-        NSString *name = [_PlexServicesInfo[row] objectForKey:@"name"];
-        NSString *hostName = [_PlexServicesInfo[row] objectForKey:@"hostName"];
-        NSString *portNum = [_PlexServicesInfo[row] objectForKey:@"port"];
-        VLCLocalPlexFolderListViewController *targetViewController = [[VLCLocalPlexFolderListViewController alloc] initWithPlexServer:name serverAddress:hostName portNumber:portNum atPath:@""];
-        [[self navigationController] pushViewController:targetViewController animated:YES];
+        NSInteger totalRow = [tableView numberOfRowsInSection:section];
+        if (row == (totalRow - 1)) {
+            VLCPlexConnectServerViewController *_connectPlexServer;
+            if (SYSTEM_RUNS_IOS7_OR_LATER)
+                _connectPlexServer = [[VLCPlexConnectServerViewController alloc] initWithNibName:@"VLCFuturePlexConnectServerViewController" bundle:nil];
+            else
+                _connectPlexServer = [[VLCPlexConnectServerViewController alloc] initWithNibName:@"VLCPlexConnectServerViewController" bundle:nil];
+
+            UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:_connectPlexServer];
+            [navCon loadTheme];
+            navCon.navigationBarHidden = NO;
+
+            [self.navigationController pushViewController:_connectPlexServer animated:YES];
+        } else {
+            NSString *name = [_PlexServicesInfo[row] objectForKey:@"name"];
+            NSString *hostName = [_PlexServicesInfo[row] objectForKey:@"hostName"];
+            NSString *portNum = [_PlexServicesInfo[row] objectForKey:@"port"];
+            VLCLocalPlexFolderListViewController *targetViewController = [[VLCLocalPlexFolderListViewController alloc] initWithPlexServer:name serverAddress:hostName portNumber:portNum atPath:@""];
+            [[self navigationController] pushViewController:targetViewController animated:YES];
+        }
     } else if (section == 2) {
         UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:_loginViewController];
         [navCon loadTheme];

+ 30 - 0
Sources/VLCPlexConnectServerViewController.h

@@ -0,0 +1,30 @@
+/*****************************************************************************
+ * VLCPlexConnectServerViewController.h
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ *
+ * Authors: Felix Paul Kühne <fkuehne # videolan.org>
+ *          Pierre SAGASPE <pierre.sagaspe # me.com>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+@interface VLCPlexConnectServerViewController : UIViewController
+
+@property (nonatomic, strong) IBOutlet UITextField *serverAddressField;
+@property (nonatomic, strong) IBOutlet UIButton *connectButton;
+@property (nonatomic, strong) IBOutlet UITextField *portField;
+@property (nonatomic, strong) IBOutlet UILabel *serverAddressLabel;
+@property (nonatomic, strong) IBOutlet UILabel *portLabel;
+@property (nonatomic, strong) IBOutlet UILabel *serverAddressHelpLabel;
+@property (nonatomic, strong) IBOutlet UITableView *serverPlexBookmark;
+@property (nonatomic, strong) IBOutlet UIButton *bookmarkButton;
+@property (strong, nonatomic) IBOutlet UILabel *bookmarkLabel;
+
+- (IBAction)connectToServer:(id)sender;
+- (IBAction)savePlexServer:(id)sender;
+
+@end

+ 198 - 0
Sources/VLCPlexConnectServerViewController.m

@@ -0,0 +1,198 @@
+/*****************************************************************************
+ * VLCPlexConnectServerViewController.m
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ *
+ * Authors: Felix Paul Kühne <fkuehne # videolan.org>
+ *          Pierre SAGASPE <pierre.sagaspe # me.com>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import "VLCPlexConnectServerViewController.h"
+#import "VLCLocalPlexFolderListViewController.h"
+#import "UIBarButtonItem+Theme.h"
+
+#define kPlexMediaServerPortDefault @"32400"
+
+@interface VLCPlexConnectServerViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
+{
+    NSMutableArray *_bookmarkServer;
+    NSMutableArray *_bookmarkPort;
+}
+@end
+
+@implementation VLCPlexConnectServerViewController
+
++ (void)initialize
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSDictionary *loginDefaults = @{kVLCPLEXServer : @[], kVLCPLEXPort : @[]};
+    [defaults registerDefaults:loginDefaults];
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+
+    self.title = @"Plex Media Server";
+    [self.connectButton setTitle:NSLocalizedString(@"BUTTON_CONNECT", nil) forState:UIControlStateNormal];
+    self.serverAddressHelpLabel.text = NSLocalizedString(@"ENTER_SERVER_ADDRESS_HELP", nil);
+    self.serverAddressLabel.text = NSLocalizedString(@"SERVER", nil);
+    self.portLabel.text = NSLocalizedString(@"SERVER_PORT", nil);
+    self.bookmarkLabel.text = NSLocalizedString(@"BOOKMARK", nil);
+
+    self.serverAddressField.delegate = self;
+    self.serverAddressField.returnKeyType = UIReturnKeyNext;
+    self.serverAddressField.clearButtonMode = UITextFieldViewModeWhileEditing;
+    self.serverAddressField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
+    self.portField.delegate = self;
+    self.portField.returnKeyType = UIReturnKeyDone;
+    self.portField.clearButtonMode = UITextFieldViewModeWhileEditing;
+    self.portField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
+
+    self.serverPlexBookmark.backgroundColor = [UIColor VLCDarkBackgroundColor];
+
+    if (SYSTEM_RUNS_IOS7_OR_LATER) {
+        UIColor *color = [UIColor VLCLightTextColor];
+        self.serverAddressField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"192.168.0.0" attributes:@{NSForegroundColorAttributeName: color}];
+        self.portField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:kPlexMediaServerPortDefault attributes:@{NSForegroundColorAttributeName: color}];
+
+        self.edgesForExtendedLayout = UIRectEdgeNone;
+    }
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    _bookmarkServer = [NSMutableArray arrayWithArray:[defaults objectForKey:kVLCPLEXServer]];
+    _bookmarkPort = [NSMutableArray arrayWithArray:[defaults objectForKey:kVLCPLEXPort]];
+
+    [super viewWillAppear:animated];
+
+    if ([defaults stringForKey:kVLCLastPLEXServer])
+        self.serverAddressField.text = [defaults stringForKey:kVLCLastPLEXServer];
+    if ([defaults stringForKey:kVLCLastPLEXPort])
+        self.portField.text = [defaults stringForKey:kVLCLastPLEXPort];
+
+    if (self.portField.text.length < 1)
+        self.portField.text = kPlexMediaServerPortDefault;
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    [defaults setObject:self.serverAddressField.text forKey:kVLCLastPLEXServer];
+    [defaults setObject:self.portField.text forKey:kVLCLastPLEXPort];
+
+    [super viewWillDisappear:animated];
+}
+
+#pragma mark - IBAction
+
+- (IBAction)connectToServer:(id)sender
+{
+    NSString *server = [NSString stringWithFormat:@"%@", self.serverAddressField.text];
+    NSString *port = [NSString stringWithFormat:@":%@", self.portField.text];
+
+    if (![port isEqualToString:@":"] && ![server isEqualToString:@""]) {
+        VLCLocalPlexFolderListViewController *targetViewController = [[VLCLocalPlexFolderListViewController alloc] initWithPlexServer:server serverAddress:server portNumber:port atPath:@""];
+        [[self navigationController] pushViewController:targetViewController animated:YES];
+    } else {
+        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:NSLocalizedString(@"INVALID_IP_PORT", nil) delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil) otherButtonTitles:nil];
+        [alert show];
+    }
+}
+
+- (IBAction)savePlexServer:(id)sender
+{
+    if (![self.serverAddressField.text isEqualToString:@""] && ![self.portField.text isEqualToString:@""]) {
+        [_bookmarkServer addObject:self.serverAddressField.text];
+        [_bookmarkPort addObject:self.portField.text];
+        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+        [defaults setObject:[NSArray arrayWithArray:_bookmarkServer] forKey:kVLCPLEXServer];
+        [defaults setObject:[NSArray arrayWithArray:_bookmarkPort] forKey:kVLCPLEXPort];
+        [defaults synchronize];
+        [self.serverPlexBookmark reloadData];
+    } else {
+        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:NSLocalizedString(@"INVALID_IP_PORT", nil) delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil) otherButtonTitles:nil];
+        [alert show];
+    }
+}
+
+#pragma mark - text view delegate
+- (BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+    if ([self.serverAddressField isFirstResponder]) {
+        [self.serverAddressField resignFirstResponder];
+        [self.portField becomeFirstResponder];
+    } else if ([self.portField isFirstResponder]) {
+        [self.portField resignFirstResponder];
+    }
+    return NO;
+}
+
+#pragma mark - table view data source
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+    return 1;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+    return _bookmarkServer.count;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    static NSString *CellIdentifier = @"PLEXbookmarkCell";
+
+    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+    if (cell == nil) {
+        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
+        cell.textLabel.textColor = [UIColor whiteColor];
+        cell.detailTextLabel.textColor = [UIColor VLCLightTextColor];
+    }
+
+    NSInteger row = indexPath.row;
+    cell.textLabel.text = _bookmarkServer[row];
+    cell.detailTextLabel.text = _bookmarkPort[row];
+
+    return cell;
+}
+
+#pragma mark - table view delegate
+
+- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor VLCDarkBackgroundColor];
+}
+
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    return YES;
+}
+
+- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    if (editingStyle == UITableViewCellEditingStyleDelete) {
+        [_bookmarkServer removeObjectAtIndex:indexPath.row];
+        [_bookmarkPort removeObjectAtIndex:indexPath.row];
+        [tableView reloadData];
+        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+        [defaults setObject:[NSArray arrayWithArray:_bookmarkServer] forKey:kVLCPLEXServer];
+        [defaults setObject:[NSArray arrayWithArray:_bookmarkPort] forKey:kVLCPLEXPort];
+        [defaults synchronize];
+    }
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    [self.serverAddressField setText:_bookmarkServer[indexPath.row]];
+    [self.portField setText:_bookmarkPort[indexPath.row]];
+
+    [self.serverPlexBookmark deselectRowAtIndexPath:indexPath animated:NO];
+}
+
+@end

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

@@ -19,6 +19,9 @@
 		265D511D1922746C00E38383 /* VLCPlexParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 265D511B1922746C00E38383 /* VLCPlexParser.m */; };
 		26D4AF8D1A78379000D5EC65 /* VLCSharedLibraryListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 26D4AF8A1A78379000D5EC65 /* VLCSharedLibraryListViewController.m */; };
 		26D4AF8E1A78379000D5EC65 /* VLCSharedLibraryParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 26D4AF8C1A78379000D5EC65 /* VLCSharedLibraryParser.m */; };
+		26E3A6FE1AAB76A100B32450 /* VLCPlexConnectServerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 26E3A6FD1AAB76A100B32450 /* VLCPlexConnectServerViewController.m */; };
+		26E3A7011AAB76D300B32450 /* VLCFuturePlexConnectServerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 26E3A6FF1AAB76D300B32450 /* VLCFuturePlexConnectServerViewController.xib */; };
+		26E3A7021AAB76D300B32450 /* VLCPlexConnectServerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 26E3A7001AAB76D300B32450 /* VLCPlexConnectServerViewController.xib */; };
 		26F1BFD01A770408001DF30C /* libMediaVLC.xml in Resources */ = {isa = PBXBuildFile; fileRef = 26F1BFCF1A770408001DF30C /* libMediaVLC.xml */; };
 		26FAE63919240C2E00A43737 /* PlexServerIcon@1x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26FAE63619240C2E00A43737 /* PlexServerIcon@1x.png */; };
 		26FAE63A19240C2E00A43737 /* PlexServerIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26FAE63719240C2E00A43737 /* PlexServerIcon@2x.png */; };
@@ -521,6 +524,10 @@
 		26D4AF8A1A78379000D5EC65 /* VLCSharedLibraryListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCSharedLibraryListViewController.m; path = Sources/VLCSharedLibraryListViewController.m; sourceTree = SOURCE_ROOT; };
 		26D4AF8B1A78379000D5EC65 /* VLCSharedLibraryParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCSharedLibraryParser.h; path = Sources/VLCSharedLibraryParser.h; sourceTree = SOURCE_ROOT; };
 		26D4AF8C1A78379000D5EC65 /* VLCSharedLibraryParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCSharedLibraryParser.m; path = Sources/VLCSharedLibraryParser.m; sourceTree = SOURCE_ROOT; };
+		26E3A6FC1AAB76A100B32450 /* VLCPlexConnectServerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCPlexConnectServerViewController.h; path = Sources/VLCPlexConnectServerViewController.h; sourceTree = SOURCE_ROOT; };
+		26E3A6FD1AAB76A100B32450 /* VLCPlexConnectServerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCPlexConnectServerViewController.m; path = Sources/VLCPlexConnectServerViewController.m; sourceTree = SOURCE_ROOT; };
+		26E3A6FF1AAB76D300B32450 /* VLCFuturePlexConnectServerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = VLCFuturePlexConnectServerViewController.xib; path = Resources/VLCFuturePlexConnectServerViewController.xib; sourceTree = SOURCE_ROOT; };
+		26E3A7001AAB76D300B32450 /* VLCPlexConnectServerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = VLCPlexConnectServerViewController.xib; path = Resources/VLCPlexConnectServerViewController.xib; sourceTree = SOURCE_ROOT; };
 		26F1BFCF1A770408001DF30C /* libMediaVLC.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = libMediaVLC.xml; sourceTree = "<group>"; };
 		26FAE63619240C2E00A43737 /* PlexServerIcon@1x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PlexServerIcon@1x.png"; sourceTree = "<group>"; };
 		26FAE63719240C2E00A43737 /* PlexServerIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PlexServerIcon@2x.png"; sourceTree = "<group>"; };
@@ -1810,6 +1817,8 @@
 				265D51191922746C00E38383 /* VLCLocalPlexFolderListViewController.m */,
 				262C71571A98FA9200F7ED34 /* VLCPlexMediaInformationViewController.h */,
 				262C71581A98FA9200F7ED34 /* VLCPlexMediaInformationViewController.m */,
+				26E3A6FC1AAB76A100B32450 /* VLCPlexConnectServerViewController.h */,
+				26E3A6FD1AAB76A100B32450 /* VLCPlexConnectServerViewController.m */,
 				26D4AF891A78379000D5EC65 /* VLCSharedLibraryListViewController.h */,
 				26D4AF8A1A78379000D5EC65 /* VLCSharedLibraryListViewController.m */,
 				7D30F3D5183AB2F100FFC021 /* VLCLocalServerListViewController.h */,
@@ -2144,6 +2153,8 @@
 				7DBBF193183AB4300009A339 /* VLCOpenNetworkStreamViewController.xib */,
 				7D89787C185DF794009BAB5D /* VLCFutureOpenNetworkStreamViewController.xib */,
 				262C715A1A98FDE300F7ED34 /* VLCPlexMediaInformationViewController.xib */,
+				26E3A6FF1AAB76D300B32450 /* VLCFuturePlexConnectServerViewController.xib */,
+				26E3A7001AAB76D300B32450 /* VLCPlexConnectServerViewController.xib */,
 			);
 			name = "Download & Network";
 			sourceTree = "<group>";
@@ -2906,6 +2917,7 @@
 				7DE18632175BA9AF006C0173 /* badgeUnread~iphone.png in Resources */,
 				7D47D6F91760CD8700E86BAD /* subtitleIcon.png in Resources */,
 				7D47D6FA1760CD8700E86BAD /* subtitleIcon@2x.png in Resources */,
+				26E3A7021AAB76D300B32450 /* VLCPlexConnectServerViewController.xib in Resources */,
 				7DF3B77719DF01550041A02E /* fsarrow-audio@2x.png in Resources */,
 				7D47D6FB1760CD8700E86BAD /* videoEffectsIcon.png in Resources */,
 				7DFC38D31A45F12F002476CB /* blank@3x.png in Resources */,
@@ -3063,6 +3075,7 @@
 				7D2A34A41805CDBA004078AA /* gradient-cell-ios7.png in Resources */,
 				7D9870281A3DECCA009CF27D /* repeatOne@3x.png in Resources */,
 				7D9870421A3DEDD1009CF27D /* Settings@3x.png in Resources */,
+				26E3A7011AAB76D300B32450 /* VLCFuturePlexConnectServerViewController.xib in Resources */,
 				7DC72D5F17B7E7C7008A26D0 /* download.png in Resources */,
 				7D27EC0E19DF30CB00EF0370 /* filledcloud.png in Resources */,
 				7D897877185DEF79009BAB5D /* repeatOne@2x.png in Resources */,
@@ -3272,6 +3285,7 @@
 				7D3784CC183A99BA009EE944 /* PAPasscodeViewController.m in Sources */,
 				7D3784E9183A9A15009EE944 /* main.m in Sources */,
 				7D30F3C2183AB24C00FFC021 /* VLCHTTPConnection.m in Sources */,
+				26E3A6FE1AAB76A100B32450 /* VLCPlexConnectServerViewController.m in Sources */,
 				7D30F3C3183AB24C00FFC021 /* VLCHTTPFileDownloader.m in Sources */,
 				41273A3C1A955C4100A2EF77 /* VLCMigrationViewController.m in Sources */,
 				7D30F3C4183AB24C00FFC021 /* VLCHTTPUploaderController.m in Sources */,