diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..d821048
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kplayer.iml b/.idea/kplayer.iml
new file mode 100644
index 0000000..cb345e8
--- /dev/null
+++ b/.idea/kplayer.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..8662aa9
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..e96c31c
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
new file mode 100644
index 0000000..922003b
--- /dev/null
+++ b/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/xcode.xml b/.idea/xcode.xml
new file mode 100644
index 0000000..341efb1
--- /dev/null
+++ b/.idea/xcode.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Podfile b/Podfile
new file mode 100644
index 0000000..d1af914
--- /dev/null
+++ b/Podfile
@@ -0,0 +1,7 @@
+platform :ios, '8.3'
+
+source 'https://github.com/CocoaPods/Specs.git'
+use_frameworks!
+
+# pod 'Player'
+pod 'Alamofire', '~> 1.2'
diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj
index 9fc6752..438cd7d 100644
--- a/kplayer.xcodeproj/project.pbxproj
+++ b/kplayer.xcodeproj/project.pbxproj
@@ -7,6 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
+ 1C736261CBA1D13D16DCBAFB /* VideoPlayerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366EF59D75216EBC0D3F0 /* VideoPlayerController.swift */; };
+ 1C73635138BBD2BB480A308F /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C736777456388CA571DA17B /* MediaPlayer.framework */; };
+ 1C7363D9DC8F9D1F866DE935 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C7368DC7EF11A553145E169 /* Kirschkeks-256x256.png */; };
+ 1C73641627BE29D9FA819F3C /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73649CEA7BDD2AC1496F76 /* LayoutTools.swift */; };
+ 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; };
+ 1C736C90DB50C4FDED266C3D /* ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7362C647AEBF03F5FD9FEB /* ItemCell.swift */; };
+ 1C736FB92B19FE17E4357C85 /* MediaItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73688DAB88F9360FB62A49 /* MediaItem.swift */; };
+ A5D637AE4588AAB5DC1CBC6B /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 127AC1F28342F9AAE3CEC5C2 /* Pods.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
C98AF5D51B124D6A00D196CC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98AF5D41B124D6A00D196CC /* AppDelegate.swift */; };
C98AF5D81B124D6A00D196CC /* kplayer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C98AF5D61B124D6A00D196CC /* kplayer.xcdatamodeld */; };
C98AF5DA1B124D6A00D196CC /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98AF5D91B124D6A00D196CC /* MasterViewController.swift */; };
@@ -28,6 +36,16 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 127AC1F28342F9AAE3CEC5C2 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1C7362C647AEBF03F5FD9FEB /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = ""; };
+ 1C73649CEA7BDD2AC1496F76 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = ""; };
+ 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; };
+ 1C7366EF59D75216EBC0D3F0 /* VideoPlayerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerController.swift; sourceTree = ""; };
+ 1C736777456388CA571DA17B /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
+ 1C73688DAB88F9360FB62A49 /* MediaItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaItem.swift; sourceTree = ""; };
+ 1C7368DC7EF11A553145E169 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Kirschkeks-256x256.png"; sourceTree = ""; };
+ 5C6CBA548F885BF342F594EA /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; };
+ A170BFB886D61D57F7009BFC /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; };
C98AF5CF1B124D6A00D196CC /* kplayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = kplayer.app; sourceTree = BUILT_PRODUCTS_DIR; };
C98AF5D31B124D6A00D196CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
C98AF5D41B124D6A00D196CC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
@@ -47,6 +65,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ A5D637AE4588AAB5DC1CBC6B /* Pods.framework in Frameworks */,
+ 1C73635138BBD2BB480A308F /* MediaPlayer.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -60,12 +80,41 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 1C736DC8C3AFB991541A2079 /* core */ = {
+ isa = PBXGroup;
+ children = (
+ 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */,
+ 1C73688DAB88F9360FB62A49 /* MediaItem.swift */,
+ );
+ path = core;
+ sourceTree = "";
+ };
+ 8CB608B13A2BDFA9D708982B /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 127AC1F28342F9AAE3CEC5C2 /* Pods.framework */,
+ 1C736777456388CA571DA17B /* MediaPlayer.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 8EB26707CC8BD3E10F328A9E /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 5C6CBA548F885BF342F594EA /* Pods.debug.xcconfig */,
+ A170BFB886D61D57F7009BFC /* Pods.release.xcconfig */,
+ );
+ name = Pods;
+ sourceTree = "";
+ };
C98AF5C61B124D6A00D196CC = {
isa = PBXGroup;
children = (
C98AF5D11B124D6A00D196CC /* kplayer */,
C98AF5EC1B124D6A00D196CC /* kplayerTests */,
C98AF5D01B124D6A00D196CC /* Products */,
+ 8EB26707CC8BD3E10F328A9E /* Pods */,
+ 8CB608B13A2BDFA9D708982B /* Frameworks */,
);
sourceTree = "";
};
@@ -89,6 +138,11 @@
C98AF5E21B124D6A00D196CC /* LaunchScreen.xib */,
C98AF5D61B124D6A00D196CC /* kplayer.xcdatamodeld */,
C98AF5D21B124D6A00D196CC /* Supporting Files */,
+ 1C736DC8C3AFB991541A2079 /* core */,
+ 1C7362C647AEBF03F5FD9FEB /* ItemCell.swift */,
+ 1C73649CEA7BDD2AC1496F76 /* LayoutTools.swift */,
+ 1C7368DC7EF11A553145E169 /* Kirschkeks-256x256.png */,
+ 1C7366EF59D75216EBC0D3F0 /* VideoPlayerController.swift */,
);
path = kplayer;
sourceTree = "";
@@ -125,9 +179,12 @@
isa = PBXNativeTarget;
buildConfigurationList = C98AF5F31B124D6A00D196CC /* Build configuration list for PBXNativeTarget "kplayer" */;
buildPhases = (
+ FA5E42A873B9445C28CCC025 /* Check Pods Manifest.lock */,
C98AF5CB1B124D6A00D196CC /* Sources */,
C98AF5CC1B124D6A00D196CC /* Frameworks */,
C98AF5CD1B124D6A00D196CC /* Resources */,
+ 81E453B289EA9615E1980098 /* Embed Pods Frameworks */,
+ F554AE493C60519505E761A6 /* Copy Pods Resources */,
);
buildRules = (
);
@@ -201,6 +258,7 @@
C98AF5DF1B124D6A00D196CC /* Main.storyboard in Resources */,
C98AF5E41B124D6A00D196CC /* LaunchScreen.xib in Resources */,
C98AF5E11B124D6A00D196CC /* Images.xcassets in Resources */,
+ 1C7363D9DC8F9D1F866DE935 /* Kirschkeks-256x256.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -213,6 +271,54 @@
};
/* End PBXResourcesBuildPhase section */
+/* Begin PBXShellScriptBuildPhase section */
+ 81E453B289EA9615E1980098 /* 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/Pods-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ F554AE493C60519505E761A6 /* Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Copy Pods Resources";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ FA5E42A873B9445C28CCC025 /* Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Check Pods Manifest.lock";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ 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;
+ };
+/* End PBXShellScriptBuildPhase section */
+
/* Begin PBXSourcesBuildPhase section */
C98AF5CB1B124D6A00D196CC /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -222,6 +328,11 @@
C98AF5D51B124D6A00D196CC /* AppDelegate.swift in Sources */,
C98AF5DA1B124D6A00D196CC /* MasterViewController.swift in Sources */,
C98AF5DC1B124D6A00D196CC /* DetailViewController.swift in Sources */,
+ 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */,
+ 1C736FB92B19FE17E4357C85 /* MediaItem.swift in Sources */,
+ 1C736C90DB50C4FDED266C3D /* ItemCell.swift in Sources */,
+ 1C73641627BE29D9FA819F3C /* LayoutTools.swift in Sources */,
+ 1C736261CBA1D13D16DCBAFB /* VideoPlayerController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -348,6 +459,7 @@
};
C98AF5F41B124D6A00D196CC /* Debug */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 5C6CBA548F885BF342F594EA /* Pods.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = kplayer/Info.plist;
@@ -358,6 +470,7 @@
};
C98AF5F51B124D6A00D196CC /* Release */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = A170BFB886D61D57F7009BFC /* Pods.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = kplayer/Info.plist;
@@ -419,6 +532,7 @@
C98AF5F51B124D6A00D196CC /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
C98AF5F61B124D6A00D196CC /* Build configuration list for PBXNativeTarget "kplayerTests" */ = {
isa = XCConfigurationList;
@@ -427,6 +541,7 @@
C98AF5F81B124D6A00D196CC /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
diff --git a/kplayer/AppDelegate.swift b/kplayer/AppDelegate.swift
index a76d230..1be94f6 100644
--- a/kplayer/AppDelegate.swift
+++ b/kplayer/AppDelegate.swift
@@ -24,7 +24,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController
let controller = masterNavigationController.topViewController as! MasterViewController
- controller.managedObjectContext = self.managedObjectContext
return true
}
diff --git a/kplayer/Base.lproj/Main.storyboard b/kplayer/Base.lproj/Main.storyboard
index b3b5a78..91a3dfb 100644
--- a/kplayer/Base.lproj/Main.storyboard
+++ b/kplayer/Base.lproj/Main.storyboard
@@ -1,7 +1,7 @@
-
+
-
+
@@ -22,7 +22,7 @@
-
+
@@ -35,7 +35,7 @@
-
+
@@ -73,7 +73,7 @@
-
+
@@ -90,7 +90,7 @@
-
+
diff --git a/kplayer/DetailViewController.swift b/kplayer/DetailViewController.swift
index 09f284f..cbdbcd0 100644
--- a/kplayer/DetailViewController.swift
+++ b/kplayer/DetailViewController.swift
@@ -7,32 +7,164 @@
//
import UIKit
+import MediaPlayer
-class DetailViewController: UIViewController {
+class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
@IBOutlet weak var detailDescriptionLabel: UILabel!
+ var moviePlayer: MPMoviePlayerController?
- var detailItem: AnyObject? {
+ var collectionView: UICollectionView!
+
+ var detailItem: MediaItem? {
didSet {
+ println(detailItem!.children)
+ if collectionView != nil {
+ collectionView.reloadData()
+ }
// Update the view.
- self.configureView()
+ // self.configureView()
}
}
+ var currentItem: MediaItem?
+
func configureView() {
// Update the user interface for the detail item.
- if let detail: AnyObject = self.detailItem {
+ println(detailItem)
+ if let detail: MediaItem = self.detailItem {
+ println(detail)
+ println(detail.children)
+
if let label = self.detailDescriptionLabel {
- label.text = detail.valueForKey("timeStamp")!.description
+ label.text = detail.name
+ }
+
+ if count(detail.children) > 0 {
+ let i = detail.children[0]
+ var url = NetworkManager.sharedInstance.playerURL(i)
+
+ println(url)
+
+// if moviePlayer == nil {
+ play(url)
+// }
}
}
+
+ }
+
+ func play(url: String) {
+ self.moviePlayer = MPMoviePlayerController()
+ if let player = self.moviePlayer {
+ NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("exitedFullscreen"), name: MPMoviePlayerDidExitFullscreenNotification, object: nil);
+ NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("enteredFullscreen"), name: MPMoviePlayerDidEnterFullscreenNotification, object: nil);
+ NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("showThumbnail:"), name: MPMoviePlayerThumbnailImageRequestDidFinishNotification, object: nil);
+
+ player.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
+ player.view.sizeToFit()
+ player.scalingMode = MPMovieScalingMode.AspectFit
+ player.controlStyle = MPMovieControlStyle.Embedded
+ player.movieSourceType = MPMovieSourceType.Streaming
+ player.repeatMode = MPMovieRepeatMode.One
+ player.contentURL = NSURL(string: url)
+ player.play()
+
+ var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("update"), userInfo: nil, repeats: false)
+
+
+ self.view.addSubview(player.view)
+ }
+ }
+
+ func showThumbnail(note: NSNotification) {
+ let userInfo = note.userInfo! as NSDictionary
+ let thumbnail = userInfo.objectForKey(MPMoviePlayerThumbnailImageKey) as! UIImage
+ let time = userInfo.objectForKey(MPMoviePlayerThumbnailTimeKey) as! NSTimeInterval
+
+ let newItem = MediaItem(name: currentItem!.name, path: currentItem!.path, root: currentItem!.root, type: ItemType.SNAPSHOT)
+ newItem.image = thumbnail
+ newItem.time = time
+ newItem.parent = currentItem!
+ currentItem!.children.append(newItem)
+
+ println (newItem.time)
+
+ }
+
+ func update() {
+ if let player = self.moviePlayer {
+ if let t = currentItem!.time {
+ player.currentPlaybackTime = t
+ }
+ else {
+ println(player.duration)
+ player.currentPlaybackTime = player.duration / 2
+ }
+
+ if currentItem!.type == ItemType.SNAPSHOT {
+ currentItem = currentItem!.parent
+ }
+
+ player.fullscreen = true
+ }
+ }
+
+ func enteredFullscreen() {
+ let mp = UIApplication.sharedApplication().keyWindow;
+ if let moviePlayerContainer = recursiveSearchForViewWithName(mp!, classname: "MPVideoContainerView") {
+ if (moviePlayerContainer.gestureRecognizers != nil) {
+ return;
+ }
+ installGestures(moviePlayerContainer)
+ }
+ }
+
+ func exitedFullscreen() {
+ moviePlayer!.view.removeFromSuperview();
+ moviePlayer = nil;
+ NSNotificationCenter.defaultCenter().removeObserver(self);
+
+ collectionView.reloadData()
+ collectionView.collectionViewLayout.invalidateLayout()
+ }
+
+ override func loadView() {
+ super.loadView()
+
+
}
override func viewDidLoad() {
super.viewDidLoad()
+
+ let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
+
+ layout.sectionInset = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
+ layout.itemSize = CGSize(width: 14 * 16 , height: 14 * 10)
+ layout.headerReferenceSize = CGSize(width: 50, height: 50)
+ collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
+
+ collectionView.dataSource = self
+ collectionView.delegate = self
+ collectionView.registerClass(ItemCell.self, forCellWithReuseIdentifier: "Cell")
+ collectionView.backgroundColor = UIColor.greenColor()
+ collectionView.registerClass(ItemCell.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderView");
+
+ view.addSubview(collectionView)
+// view.autoresizesSubviews = true
+// collectionView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
// Do any additional setup after loading the view, typically from a nib.
- self.configureView()
+
+ collectionView.reloadData()
+ }
+
+ override func viewDidAppear(animated: Bool) {
+ super.viewDidAppear(animated)
+
+// self.configureView()
+
}
override func didReceiveMemoryWarning() {
@@ -40,6 +172,146 @@ class DetailViewController: UIViewController {
// Dispose of any resources that can be recreated.
}
+ func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
+ if let detail: MediaItem = self.detailItem {
+ let cnt = count(detail.children)
+ println(cnt)
+ return cnt
+ }
+ return 0
+ }
+
+ func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ if let detail: MediaItem = self.detailItem {
+ return count(detail.children[section].children) + 1
+ }
+ return 0
+ }
+
+ func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
+ let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! ItemCell
+
+ if let detail: MediaItem = self.detailItem {
+ let items = detail.children[indexPath.section]
+ if indexPath.item >= count(items.children) {
+ cell.setItem(items)
+ } else {
+ let item = items.children[indexPath.item]
+ cell.setItem(item)
+ }
+ }
+ cell.backgroundColor = UIColor.blueColor()
+ return cell
+ }
+
+ func collectionView(collectionView: UICollectionView,
+ viewForSupplementaryElementOfKind kind: String,
+ atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
+ switch kind {
+ case UICollectionElementKindSectionHeader:
+ let headerView =
+ collectionView.dequeueReusableSupplementaryViewOfKind(kind,
+ withReuseIdentifier: "HeaderView",
+ forIndexPath: indexPath)
+ as! ItemCell
+ let items = detailItem!.children[indexPath.section]
+
+ println("sec \(indexPath.section) " + items.name)
+
+ headerView.label.text = items.name
+ headerView.backgroundColor = UIColor.yellowColor()
+ return headerView
+ default:
+ assert(false, "Unexpected element kind")
+ }
+ }
+
+ func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
+ if let detail: MediaItem = self.detailItem {
+ var items = detail.children[indexPath.section]
+ if indexPath.item >= count(items.children) {
+ println(items.name)
+ } else {
+ items = items.children[indexPath.item]
+ println(items.name)
+ }
+
+ currentItem = items
+
+ var url = NetworkManager.sharedInstance.playerURL(items)
+
+ println(url)
+
+ play(url)
+
+ }
+ }
+ func installGestures(moviePlayer: UIView) {
+ let twoFingersTwoTaps = UITapGestureRecognizer(target: self, action: "twoFingersTwoTaps")
+ twoFingersTwoTaps.numberOfTapsRequired = 2
+ twoFingersTwoTaps.numberOfTouchesRequired = 2
+ moviePlayer.addGestureRecognizer(twoFingersTwoTaps)
+
+ let sR = UISwipeGestureRecognizer(target: self, action: "swipeRight")
+ sR.direction = UISwipeGestureRecognizerDirection.Right
+ sR.numberOfTouchesRequired = 1
+ moviePlayer.addGestureRecognizer(sR)
+
+ let sL = UISwipeGestureRecognizer(target: self, action: "swipeLeft")
+ sL.direction = UISwipeGestureRecognizerDirection.Left
+ sL.numberOfTouchesRequired = 1
+ moviePlayer.addGestureRecognizer(sL)
+
+ let sR2 = UISwipeGestureRecognizer(target: self, action: "swipeDown")
+ sR2.direction = UISwipeGestureRecognizerDirection.Down
+ sR2.numberOfTouchesRequired = 1
+ moviePlayer.addGestureRecognizer(sR2)
+
+ }
+
+ func swipeRight() {
+ println("r")
+ if let player = self.moviePlayer {
+ player.currentPlaybackTime = player.currentPlaybackTime + 30.0
+ }
+ }
+
+ func swipeDown() {
+ println("r")
+ if let player = self.moviePlayer {
+ player.currentPlaybackTime = player.currentPlaybackTime + 10.0
+ }
+ }
+
+ func swipeLeft() {
+ println("l")
+ if let player = self.moviePlayer {
+ player.currentPlaybackTime = player.currentPlaybackTime - 30.0
+ }
+ }
+
+ func twoFingersTwoTaps() {
+ println("tap")
+ moviePlayer!.requestThumbnailImagesAtTimes([moviePlayer!.currentPlaybackTime],
+ timeOption:MPMovieTimeOption.NearestKeyFrame);
+ }
+
+
+ func recursiveSearchForViewWithName(view: UIView, classname: String) -> UIView? {
+ for v in view.subviews {
+ var result = recursiveSearchForViewWithName(v as! UIView, classname: classname)
+ let cn = toString(v.dynamicType)
+
+ if cn == classname {
+ return v as! UIView
+ }
+
+ if result != nil {
+ return result
+ }
+ }
+ return nil
+ }
}
diff --git a/kplayer/ItemCell.swift b/kplayer/ItemCell.swift
new file mode 100644
index 0000000..e38b99d
--- /dev/null
+++ b/kplayer/ItemCell.swift
@@ -0,0 +1,53 @@
+//
+// Created by Marco Schmickler on 25.05.15.
+// Copyright (c) 2015 Marco Schmickler. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+class ItemCell: UICollectionViewCell {
+ var item: MediaItem?
+
+ var label: UILabel!
+ var image: UIImageView!
+
+ required init(coder aDecoder: NSCoder)
+ {
+ super.init(coder: aDecoder)
+
+ }
+
+
+ override init(frame: CGRect) {
+
+ super.init(frame: frame)
+
+ label = UILabel(frame: frame)
+
+ image = UIImageView()
+ image.layer.borderWidth = 2.0;
+
+ autolayout(["label": label, "imag": image],
+ constraints:
+ "H:|-5-[label]-5-|",
+ "H:|-5-[imag]-5-|",
+ "V:|-5-[label(<=50)]-5-[imag]-5-|" )
+ }
+
+ func setItem(item: MediaItem) {
+ self.item = item
+
+ label.text = item.name
+
+ if let i = item.image {
+ println (i)
+ image.image = i
+ image.sizeToFit()
+ }
+ else {
+ image.image = UIImage(named: "Kirschkeks-256x256.png")
+ }
+
+ }
+}
diff --git a/kplayer/Kirschkeks-256x256.png b/kplayer/Kirschkeks-256x256.png
new file mode 100644
index 0000000..180be0b
Binary files /dev/null and b/kplayer/Kirschkeks-256x256.png differ
diff --git a/kplayer/LayoutTools.swift b/kplayer/LayoutTools.swift
new file mode 100644
index 0000000..9efe279
--- /dev/null
+++ b/kplayer/LayoutTools.swift
@@ -0,0 +1,45 @@
+//
+// Created by Marco Schmickler on 25.05.15.
+// Copyright (c) 2015 Marco Schmickler. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+public extension UIView {
+
+ func autolayout(views: [String:UIView!], constraints: Any...) {
+ autolay(views, constraints:constraints)
+ }
+
+ func autolay(views: [String:UIView!], constraints: [Any]) {
+ var index = 0;
+
+ for value in views.values {
+ let vv: UIView = value
+ addSubview(vv)
+ vv.setTranslatesAutoresizingMaskIntoConstraints(false)
+ }
+
+ while index < constraints.count {
+ let c = constraints[index]
+ if c is String {
+ if index + 1 < constraints.count && !(constraints[index + 1] is String) {
+ index++
+ let constr: NSArray = NSLayoutConstraint.constraintsWithVisualFormat(c as! String,
+ options: constraints[index] as! NSLayoutFormatOptions,
+ metrics: nil,
+ views: views)
+ addConstraints(constr as [AnyObject])
+ } else {
+ let constr: NSArray = NSLayoutConstraint.constraintsWithVisualFormat(c as! String,
+ options: NSLayoutFormatOptions(0), metrics: nil, views: views)
+ addConstraints(constr as [AnyObject])
+ }
+ }
+ index++
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/kplayer/MasterViewController.swift b/kplayer/MasterViewController.swift
index fcc2deb..6192f33 100644
--- a/kplayer/MasterViewController.swift
+++ b/kplayer/MasterViewController.swift
@@ -9,11 +9,11 @@
import UIKit
import CoreData
-class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate {
+class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
- var managedObjectContext: NSManagedObjectContext? = nil
+ var items = [MediaItem]()
override func awakeFromNib() {
super.awakeFromNib()
@@ -25,6 +25,28 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
override func viewDidLoad() {
super.viewDidLoad()
+
+ NetworkManager.sharedInstance.loadDirs("/srv/samba/ren/knk_archiv4") { (g) in
+ var d = Dictionary()
+
+ for f in g {
+ var p = d[f.path]
+
+ if p == nil {
+ p = MediaItem(name: "", path: f.path, root: f.root, type: ItemType.FOLDER)
+ d[f.path] = p!
+ println(p)
+ }
+
+ p!.children.append(f)
+ f.parent = p!
+ }
+
+ self.items = Array(d.values)
+
+ self.tableView.reloadData()
+ }
+
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
@@ -42,22 +64,6 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
}
func insertNewObject(sender: AnyObject) {
- let context = self.fetchedResultsController.managedObjectContext
- let entity = self.fetchedResultsController.fetchRequest.entity!
- let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as! NSManagedObject
-
- // If appropriate, configure the new managed object.
- // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
- newManagedObject.setValue(NSDate(), forKey: "timeStamp")
-
- // Save the context.
- var error: NSError? = nil
- if !context.save(&error) {
- // Replace this implementation with code to handle the error appropriately.
- // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
- //println("Unresolved error \(error), \(error.userInfo)")
- abort()
- }
}
// MARK: - Segues
@@ -65,7 +71,7 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
- let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
+ let object = items[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
@@ -77,12 +83,11 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
- return self.fetchedResultsController.sections?.count ?? 0
+ return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
- return sectionInfo.numberOfObjects
+ return count(items)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
@@ -93,111 +98,19 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
- return true
+ return false
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
- let context = self.fetchedResultsController.managedObjectContext
- context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
-
- var error: NSError? = nil
- if !context.save(&error) {
- // Replace this implementation with code to handle the error appropriately.
- // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
- //println("Unresolved error \(error), \(error.userInfo)")
- abort()
- }
}
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
- let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
- cell.textLabel!.text = object.valueForKey("timeStamp")!.description
- }
-
- // MARK: - Fetched results controller
-
- var fetchedResultsController: NSFetchedResultsController {
- if _fetchedResultsController != nil {
- return _fetchedResultsController!
- }
-
- let fetchRequest = NSFetchRequest()
- // Edit the entity name as appropriate.
- let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
- fetchRequest.entity = entity
-
- // Set the batch size to a suitable number.
- fetchRequest.fetchBatchSize = 20
-
- // Edit the sort key as appropriate.
- let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
- let sortDescriptors = [sortDescriptor]
-
- fetchRequest.sortDescriptors = [sortDescriptor]
-
- // Edit the section name key path and cache name if appropriate.
- // nil for section name key path means "no sections".
- let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
- aFetchedResultsController.delegate = self
- _fetchedResultsController = aFetchedResultsController
-
- var error: NSError? = nil
- if !_fetchedResultsController!.performFetch(&error) {
- // Replace this implementation with code to handle the error appropriately.
- // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
- //println("Unresolved error \(error), \(error.userInfo)")
- abort()
- }
-
- return _fetchedResultsController!
- }
- var _fetchedResultsController: NSFetchedResultsController? = nil
-
- func controllerWillChangeContent(controller: NSFetchedResultsController) {
- self.tableView.beginUpdates()
- }
-
- func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
- switch type {
- case .Insert:
- self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
- case .Delete:
- self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
- default:
- return
- }
- }
-
- func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
- switch type {
- case .Insert:
- tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
- case .Delete:
- tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
- case .Update:
- self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!)
- case .Move:
- tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
- tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
- default:
- return
- }
- }
-
- func controllerDidChangeContent(controller: NSFetchedResultsController) {
- self.tableView.endUpdates()
+ let object = items[indexPath.row]
+ cell.textLabel!.text = object.path
}
- /*
- // Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
-
- func controllerDidChangeContent(controller: NSFetchedResultsController) {
- // In the simplest, most efficient, case, reload the table view.
- self.tableView.reloadData()
- }
- */
}
diff --git a/kplayer/VideoPlayerController.swift b/kplayer/VideoPlayerController.swift
new file mode 100644
index 0000000..5cd3e88
--- /dev/null
+++ b/kplayer/VideoPlayerController.swift
@@ -0,0 +1,9 @@
+//
+// Created by Marco Schmickler on 26.05.15.
+// Copyright (c) 2015 Marco Schmickler. All rights reserved.
+//
+
+import Foundation
+
+class VideoPlayerController {
+}
diff --git a/kplayer/core/MediaItem.swift b/kplayer/core/MediaItem.swift
new file mode 100644
index 0000000..d85044d
--- /dev/null
+++ b/kplayer/core/MediaItem.swift
@@ -0,0 +1,38 @@
+//
+// Created by Marco Schmickler on 25.05.15.
+// Copyright (c) 2015 Marco Schmickler. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+enum ItemType {
+ case FOLDER
+ case VIDEO
+ case SNAPSHOT
+}
+
+class MediaItem : DebugPrintable {
+ var name : String
+ var path : String
+ var root: String
+
+ var image: UIImage?
+ var time: NSTimeInterval?
+
+ var children: [MediaItem]
+ var parent: MediaItem?
+ var type: ItemType
+
+ init(name: String, path: String, root: String, type: ItemType) {
+ self.name = name
+ self.path = path
+ self.root = root
+ self.type = type
+ children = [MediaItem]()
+ }
+
+ var debugDescription: String {
+ return root + " " + path + " " + name
+ }
+}
diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift
new file mode 100644
index 0000000..477a910
--- /dev/null
+++ b/kplayer/core/NetworkManager.swift
@@ -0,0 +1,72 @@
+//
+// Created by Marco Schmickler on 24.05.15.
+// Copyright (c) 2015 Marco Schmickler. All rights reserved.
+//
+
+import Foundation
+import Alamofire
+
+class NetworkManager {
+ static let sharedInstance = NetworkManager()
+
+ let baseurl = "http://linkstation/tomcat/media"
+
+ func loadRoots() -> AnyObject {
+ var itemsArray = [];
+
+ Alamofire.request(.GET, baseurl + "/service/roots")
+ .responseString {
+ (_, _, string, _) in
+ let f = split(string!) {
+ $0 == "\r\n"
+ }
+ println(string)
+ println(f)
+ }
+
+// .response { (request, response, data, error) in
+// println(request)
+// println(response)
+//
+// var r = (response as NSString).componentsSeparatedByString("\r\n")
+//
+// println(error)
+// }
+
+ return itemsArray;
+ }
+
+ func loadDirs(root: String, completionHandler: ([MediaItem]) -> Void) -> Void {
+ var itemsArray = [];
+
+ Alamofire.request(.GET, baseurl + "/service/dirs" + root)
+ .responseString {
+ (_, _, string, _) in
+ let f = split(string!) {
+ $0 == "\r\n"
+ }
+
+ let len = count(root)
+
+ var res = [MediaItem]()
+
+ for s in f {
+ if (s.hasSuffix(".mp4")) {
+ let l = count(s)
+ let name = (s as NSString).lastPathComponent
+ let pathlen = l - len - count(name)
+ let path = (s as NSString).substringWithRange(NSMakeRange(len + 1, pathlen - 2))
+ let i = MediaItem(name: name, path: path, root: root, type: ItemType.VIDEO)
+ if !name.hasPrefix(".") {
+ res.append(i)
+ }
+ }
+ }
+ completionHandler(res)
+ }
+ }
+
+ func playerURL(item: MediaItem) -> String {
+ return baseurl + "/service/stream" + item.root + "/" + item.path + "/" + item.name
+ }
+}