diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index 2b9bbf0..645415d 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -7,20 +7,36 @@ objects = { /* Begin PBXBuildFile section */ + 1C7361B71E73ECBE5891B755 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73649A4ADDAAAED634F537 /* Dictionary.swift */; }; + 1C7361F0F80FEC5B72946923 /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73605289164DF119E60720 /* Array.swift */; }; + 1C73626E34BA4D64ACF94AE5 /* alamoimage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C60C423AE87B8D0F22A /* alamoimage.swift */; }; 1C73631EACF56BABD3B2BCFB /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */; }; + 1C7363402AB2B0E5CA0B35BF /* Float.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365A537608EDDE897BF7C /* Float.swift */; }; 1C73635138BBD2BB480A308F /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C736777456388CA571DA17B /* MediaPlayer.framework */; }; + 1C7363D241B56AEE78D82FAD /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73666529EEEBD464F193CD /* Sequence.swift */; }; 1C7363D9DC8F9D1F866DE935 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C7368DC7EF11A553145E169 /* Kirschkeks-256x256.png */; }; 1C73640D928DE56D35175D39 /* UploadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736260E748CF136FF37EA7 /* UploadOperation.swift */; }; 1C73646F87B495A47D7943C7 /* NetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369EC16B19B32B515169E /* NetData.swift */; }; + 1C7364F64DCE93FCF4671D98 /* Character.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369BCA71AAF64F874F946 /* Character.swift */; }; 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; }; 1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; }; 1C7365885FAF292F2221ED44 /* MediaPhotoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73673DC671535E3A049F54 /* MediaPhotoController.swift */; }; + 1C73677CD719D0F82D144BF6 /* Bool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C43137E3B8391E5F07B /* Bool.swift */; }; 1C7368364397315E12E90F05 /* VideoPlayerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7367379DEE94EBF3FAFA78 /* VideoPlayerController.swift */; }; 1C73688D13E5A804880C8768 /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */; }; + 1C73689C7E6A25E758B16C54 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73653A9EBCC466FD4E3DA6 /* String.swift */; }; 1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.swift */; }; 1C7369ABC44CFB530EA71FB6 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */; }; + 1C7369C330F1AB1E4F166050 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736E7B8BAD989DC544088E /* Range.swift */; }; + 1C736A88E2CF46197FAE3160 /* NSDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360E65B741C57C034FC7C /* NSDate.swift */; }; 1C736C5DD23466269607E07F /* alamojson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73618272969871601AB817 /* alamojson.swift */; }; + 1C736C5EA9EEFF40AD42CA60 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736BCC6787411AA435D1E6 /* Double.swift */; }; + 1C736C885C8EE7CFBF3B8DC3 /* ExSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365E164CCEDB0A9FDF7FA /* ExSwift.swift */; }; + 1C736C9C89BABC38C80025FE /* Int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7368956B34717DAFC35EFF /* Int.swift */; }; + 1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; }; + 1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */; }; 1C736D24B49451141CD4B64D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */; }; + 1C736F3BC10D1A5ECEA50A30 /* NSArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DB8164E3EF18FA6F28D /* NSArray.swift */; }; 1C736F6A223D4ADB2E1BA733 /* ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736069C214E9522BB1BD97 /* 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, ); }; }; @@ -44,22 +60,39 @@ /* Begin PBXFileReference section */ 127AC1F28342F9AAE3CEC5C2 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1C73605289164DF119E60720 /* Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; 1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = ""; }; + 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanekeFetchOperation.swift; sourceTree = ""; }; + 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoadOperation.swift; sourceTree = ""; }; + 1C7360E65B741C57C034FC7C /* NSDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDate.swift; sourceTree = ""; }; + 1C7361024E8CCBDE9C052658 /* ExSwift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExSwift.h; sourceTree = ""; }; 1C73611D226B48C24DB37535 /* MasterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = ""; }; 1C73618272969871601AB817 /* alamojson.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = alamojson.swift; sourceTree = ""; }; 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkHelper.swift; sourceTree = ""; }; 1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = ""; }; + 1C73649A4ADDAAAED634F537 /* Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; + 1C73653A9EBCC466FD4E3DA6 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; + 1C7365A537608EDDE897BF7C /* Float.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Float.swift; sourceTree = ""; }; 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; + 1C7365E164CCEDB0A9FDF7FA /* ExSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExSwift.swift; sourceTree = ""; }; + 1C73666529EEEBD464F193CD /* Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = ""; }; 1C7367379DEE94EBF3FAFA78 /* VideoPlayerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerController.swift; sourceTree = ""; }; 1C73673DC671535E3A049F54 /* MediaPhotoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPhotoController.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 = ""; }; + 1C7368956B34717DAFC35EFF /* Int.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Int.swift; sourceTree = ""; }; 1C7368DC7EF11A553145E169 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Kirschkeks-256x256.png"; sourceTree = ""; }; + 1C7369BCA71AAF64F874F946 /* Character.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Character.swift; sourceTree = ""; }; 1C7369EC16B19B32B515169E /* NetData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetData.swift; sourceTree = ""; }; 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = ""; }; + 1C736BCC6787411AA435D1E6 /* Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = ""; }; + 1C736C43137E3B8391E5F07B /* Bool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bool.swift; sourceTree = ""; }; + 1C736C60C423AE87B8D0F22A /* alamoimage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = alamoimage.swift; sourceTree = ""; }; 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = ""; }; + 1C736DB8164E3EF18FA6F28D /* NSArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSArray.swift; sourceTree = ""; }; 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; + 1C736E7B8BAD989DC544088E /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; 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; }; @@ -121,6 +154,27 @@ path = master; sourceTree = ""; }; + 1C73654BC4A499E5D104B61F /* exswift */ = { + isa = PBXGroup; + children = ( + 1C7369BCA71AAF64F874F946 /* Character.swift */, + 1C736C43137E3B8391E5F07B /* Bool.swift */, + 1C736DB8164E3EF18FA6F28D /* NSArray.swift */, + 1C736BCC6787411AA435D1E6 /* Double.swift */, + 1C73653A9EBCC466FD4E3DA6 /* String.swift */, + 1C7361024E8CCBDE9C052658 /* ExSwift.h */, + 1C7368956B34717DAFC35EFF /* Int.swift */, + 1C7365A537608EDDE897BF7C /* Float.swift */, + 1C7360E65B741C57C034FC7C /* NSDate.swift */, + 1C73666529EEEBD464F193CD /* Sequence.swift */, + 1C7365E164CCEDB0A9FDF7FA /* ExSwift.swift */, + 1C73649A4ADDAAAED634F537 /* Dictionary.swift */, + 1C736E7B8BAD989DC544088E /* Range.swift */, + 1C73605289164DF119E60720 /* Array.swift */, + ); + path = exswift; + sourceTree = ""; + }; 1C7365603CAE04E39B73D843 /* util */ = { isa = PBXGroup; children = ( @@ -130,6 +184,9 @@ 1C736260E748CF136FF37EA7 /* UploadOperation.swift */, 1C7369EC16B19B32B515169E /* NetData.swift */, 1C73618272969871601AB817 /* alamojson.swift */, + 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */, + 1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */, + 1C736C60C423AE87B8D0F22A /* alamoimage.swift */, ); path = util; sourceTree = ""; @@ -196,6 +253,7 @@ 1C73615846EE8B07DAAFD230 /* detail */, 1C7364808E72BFA7575E75E1 /* master */, 1C7363B608460DED4F522D1C /* photo */, + 1C73654BC4A499E5D104B61F /* exswift */, ); path = kplayer; sourceTree = ""; @@ -393,6 +451,22 @@ 1C736C5DD23466269607E07F /* alamojson.swift in Sources */, 1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */, 1C7365885FAF292F2221ED44 /* MediaPhotoController.swift in Sources */, + 1C7364F64DCE93FCF4671D98 /* Character.swift in Sources */, + 1C73677CD719D0F82D144BF6 /* Bool.swift in Sources */, + 1C736F3BC10D1A5ECEA50A30 /* NSArray.swift in Sources */, + 1C736C5EA9EEFF40AD42CA60 /* Double.swift in Sources */, + 1C73689C7E6A25E758B16C54 /* String.swift in Sources */, + 1C736C9C89BABC38C80025FE /* Int.swift in Sources */, + 1C7363402AB2B0E5CA0B35BF /* Float.swift in Sources */, + 1C736A88E2CF46197FAE3160 /* NSDate.swift in Sources */, + 1C7363D241B56AEE78D82FAD /* Sequence.swift in Sources */, + 1C736C885C8EE7CFBF3B8DC3 /* ExSwift.swift in Sources */, + 1C7361B71E73ECBE5891B755 /* Dictionary.swift in Sources */, + 1C7369C330F1AB1E4F166050 /* Range.swift in Sources */, + 1C7361F0F80FEC5B72946923 /* Array.swift in Sources */, + 1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */, + 1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */, + 1C73626E34BA4D64ACF94AE5 /* alamoimage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kplayer/AppDelegate.swift b/kplayer/AppDelegate.swift index 1be94f6..6bd6cff 100644 --- a/kplayer/AppDelegate.swift +++ b/kplayer/AppDelegate.swift @@ -24,6 +24,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController let controller = masterNavigationController.topViewController as! MasterViewController + + controller.items = [ + MediaItem(name: "fav", path:"fav", root: "/srv/samba/ren/fav", type: ItemType.ROOT), + MediaItem(name: "knk_archiv", path:"knk_archiv", root: "/srv/samba/ren/knk_archiv", type: ItemType.ROOT), + MediaItem(name: "knk_archiv2", path:"knk_archiv2", root: "/srv/samba/ren/knk_archiv2", type: ItemType.ROOT), + MediaItem(name: "knk_archiv3", path:"knk_archiv3", root: "/srv/samba/ren/knk_archiv3", type: ItemType.ROOT), + MediaItem(name: "knk_archiv4", path:"knk_archiv4", root: "/srv/samba/ren/knk_archiv4", type: ItemType.ROOT), + MediaItem(name: "sp", path:"sp", root: "/srv/samba/ren/sp", type: ItemType.ROOT), + MediaItem(name: "fjoy", path:"fjoy", root: "/srv/samba/ren/fjoy", type: ItemType.ROOT), + MediaItem(name: "heg", path:"heg", root: "/srv/samba/ren/heg", type: ItemType.ROOT), + MediaItem(name: "ten", path:"ten", root: "/srv/samba/ren/ten", type: ItemType.ROOT) + ] + return true } diff --git a/kplayer/Base.lproj/Main.storyboard b/kplayer/Base.lproj/Main.storyboard index 10d3a21..72d7782 100644 --- a/kplayer/Base.lproj/Main.storyboard +++ b/kplayer/Base.lproj/Main.storyboard @@ -131,7 +131,7 @@ - + diff --git a/kplayer/core/MediaItem.swift b/kplayer/core/MediaItem.swift index 73a5a85..52fb3a5 100644 --- a/kplayer/core/MediaItem.swift +++ b/kplayer/core/MediaItem.swift @@ -7,8 +7,10 @@ import Foundation import UIKit enum ItemType : String, Printable { + case ROOT = "root" case FOLDER = "folder" case VIDEO = "video" + case PICS = "pics" case SNAPSHOT = "snapshot" var description: String { @@ -33,25 +35,32 @@ class MediaItem : DebugPrintable { var loaded = false var featured = true + var sortName = "" + init(name: String, path: String, root: String, type: ItemType) { self.name = name self.path = path self.root = root self.type = type + children = [MediaItem]() + + sortName = computeSortName(name) } - var sortName: String { - var sortName = name.stringByReplacingOccurrencesOfString("full", withString: "") + func computeSortName(sname: String) -> String { + var lsortName = sname.stringByReplacingOccurrencesOfString("full", withString: "") var fullNameArr = split(sortName) {$0 == "_"} +println (lsortName) + if !fullNameArr.isEmpty { + lsortName = fullNameArr[0] + } - sortName = fullNameArr[0] - - while count(sortName) < 6 { - sortName = "0" + sortName + while count(lsortName) < 6 { + lsortName = "0" + lsortName } - return sortName + return lsortName } var thumbPath: String { @@ -67,7 +76,8 @@ class MediaItem : DebugPrintable { } var imageUrlAbsolute: String { - let imageUrl = thumbUrl!.stringByReplacingOccurrencesOfString("_thumb.jpg", withString: ".jpg") + var imageUrl = thumbUrl!.stringByReplacingOccurrencesOfString("_thumb.jpg", withString: ".jpg") + imageUrl = imageUrl.stringByReplacingOccurrencesOfString("?preview=true", withString: "") return NetworkManager.sharedInstance.baseurl + "/service/download" + imageUrl } @@ -78,6 +88,6 @@ class MediaItem : DebugPrintable { } var debugDescription: String { - return root + " " + path + " " + name + return "\(type): \(root) \(path) \(name) (\(index)) Child count \(count(children))" } } diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift index 083ad34..01b9915 100644 --- a/kplayer/core/NetworkManager.swift +++ b/kplayer/core/NetworkManager.swift @@ -10,7 +10,7 @@ import SwiftyJSON class NetworkManager { static let sharedInstance = NetworkManager() - let baseurl = "http://linkstation/tomcat/media" + let baseurl = "http://linkstation.local/tomcat/media" lazy var operationQueue: NSOperationQueue = { var queue = NSOperationQueue() @@ -45,10 +45,10 @@ class NetworkManager { } func loadDirs(root: String, completionHandler: ([MediaItem]) -> Void) -> Void { - let url = baseurl + "/service/listvideos" + root + let url1 = baseurl + "/service/listvideos" + root let len = count(root) var res = [MediaItem]() - + let url = (url1 as NSString).stringByReplacingOccurrencesOfString(" ", withString: "+") println(url) Alamofire.request(.GET, url).responseSwiftyJSON({ @@ -59,16 +59,16 @@ class NetworkManager { for (a, c) in json { // let (a,b) = j # let s = c.object as! String - + println(s) if s.hasSuffix(".mp4") || s.hasSuffix(".m4v") { let l = count(s) let name = (s as NSString).lastPathComponent var pathlen = l - len - count(name) // if (pathlen > 1000) { - println (pathlen) - println (name) - println (s) + println(pathlen) + println(name) + println(s) // } if (pathlen < 2) { pathlen = 2 @@ -85,10 +85,70 @@ class NetworkManager { }) } + func loadPicDirs(root: String, completionHandler: ([MediaItem]) -> Void) -> Void { + let url1 = baseurl + "/service/listpicdirs" + root + let len = count(root) + var res = [MediaItem]() + let url = (url1 as NSString).stringByReplacingOccurrencesOfString(" ", withString: "+") + println(url) + + Alamofire.request(.GET, url).responseSwiftyJSON({ + (request, response, json, error) in + + var hashes = NSMutableSet() + var items = Dictionary() + + for (a, c) in json { + // let (a,b) = j # + let s = c.object as! String + println(s) + if s.hasSuffix(".jpg") { + let l = count(s) + let name = (s as NSString).lastPathComponent + var pathlen = l - len - count(name) + +// if (pathlen > 1000) { + println(pathlen) + println(name) + println(s) +// } + if (pathlen < 2) { + pathlen = 2 + } + + let path = (s as NSString).substringWithRange(NSMakeRange(len + 1, pathlen - 2)) + + let folderName = path.lastPathComponent + let fl = count(path) + let pfl = fl - count(folderName) + + println ("\(folderName) \(pfl)") + let fpath = (path as NSString).substringWithRange(NSMakeRange(0, pfl)) + + let i = MediaItem(name: folderName, path: fpath, root: root, type: ItemType.PICS) + i.thumbUrl = "\(s)?preview=true" + if !name.hasPrefix(".") { + if let picfolder = items[path] { + picfolder.children.append(i) + } + else { + let iff = MediaItem(name: folderName, path: fpath, root: root, type: ItemType.PICS) + items[path] = iff + res.append(iff) + iff.children.append(i) + } + } + } + } + completionHandler(res) + }) + } + func loadDir(root: String, completionHandler: ([MediaItem]) -> Void) -> Void { var itemsArray = []; - Alamofire.request(.GET, baseurl + "/service/dirs" + root) + let url = (root as NSString).stringByReplacingOccurrencesOfString(" ", withString: "+") + Alamofire.request(.GET, baseurl + "/service/dirs" + url) .responseString { (_, _, string, _) in let f = split(string!) { @@ -115,8 +175,35 @@ class NetworkManager { } } + func listDirs(root: String, completionHandler: ([MediaItem]) -> Void) -> Void { + var itemsArray = []; + let len = count(root) + let url = (root as NSString).stringByReplacingOccurrencesOfString(" ", withString: "+") + + let ux = baseurl + "/service/listdirs" + url +println (ux) + Alamofire.request(.GET, ux) + .responseSwiftyJSON({ + (request, response, json, error) in + + var res = [MediaItem]() + + for (a, c) in json { + println (c.object) + + // let (a,b) = j # + let s = (c.object as! NSString).substringFromIndex(len+1) + println (s) + + let i = MediaItem(name: s, path: s, root: root, type: ItemType.FOLDER) + res.append(i) + } + completionHandler(res) + }) + } + func playerURL(item: MediaItem) -> String { - let enc = item.name // .stringByReplacingOccurrencesOfString(" ", withString: "+") + let enc = item.name.stringByReplacingOccurrencesOfString(" ", withString: "%20") return baseurl + "/service/stream" + item.root + "/" + item.path + "/" + enc } @@ -129,6 +216,8 @@ class NetworkManager { return } +// Alamofire.Manager.sharedInstance.session.invalidateAndCancel() + var j = 0 for i in item.children { loadItem(i, index: j) @@ -153,10 +242,10 @@ class NetworkManager { println(url) - Alamofire.request(.GET, url).responseSwiftyJSON( { + Alamofire.request(.GET, url).responseSwiftyJSON({ (request, response, json, error) in - var hashes = Dictionary() + var hashes = Dictionary() for (a, c) in json { // let (a,b) = j # @@ -180,7 +269,7 @@ class NetworkManager { for (ts, p) in hashes { let t = (ts as NSString).doubleValue / 1000 - let snap = MediaItem(name: item.name, path: item.path, root: item.root, type:.SNAPSHOT) + let snap = MediaItem(name: item.name, path: item.path, root: item.root, type: .SNAPSHOT) snap.time = t snap.thumbUrl = p @@ -193,7 +282,7 @@ class NetworkManager { } item.loaded = true - NSNotificationCenter.defaultCenter().postNotificationName("loadedItems", object: index) + NSNotificationCenter.defaultCenter().postNotificationName("loadedItems", object: item) // println(error) }) } @@ -205,7 +294,7 @@ class NetworkManager { } func saveItem(item: MediaItem) { - println (item.children) + println(item.children) if (item.type != ItemType.VIDEO) { @@ -216,7 +305,7 @@ class NetworkManager { println(url) - Alamofire.request(.GET, url).responseSwiftyJSON( { + Alamofire.request(.GET, url).responseSwiftyJSON({ (request, response, json, error) in var hashes = NSMutableSet() @@ -233,11 +322,10 @@ class NetworkManager { let ms = Int(t * 1000) let p = c.thumbPath + "\(ms).jpg" let pt = c.thumbPath + "\(ms)_thumb.jpg" - println (p) + println(p) if hashes.containsObject(pt) { - println ("contained") - } - else { + println("contained") + } else { let imageData = UIImageJPEGRepresentation(c.image, 1.0); let op = UploadOperation(baseUrl: self.baseurl + "/service/upload", data: imageData, path: p) self.operationQueue.addOperation(op) @@ -256,7 +344,7 @@ class NetworkManager { } for o in hashes { - println ("To delete \(o)") + println("To delete \(o)") Alamofire.request(.GET, self.baseurl + "/service/deletethumb\(o)") } diff --git a/kplayer/detail/DetailViewController.swift b/kplayer/detail/DetailViewController.swift index 73dc989..5b32765 100644 --- a/kplayer/detail/DetailViewController.swift +++ b/kplayer/detail/DetailViewController.swift @@ -7,6 +7,7 @@ // import UIKit +import Alamofire class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { @@ -17,6 +18,8 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout var currentItem: MediaItem? + var defaultItemSize = CGSize(width: (15 * 16) - 6, height: 15 * 9) + var detailItem: MediaItem? { didSet { println(detailItem!.children) @@ -56,10 +59,18 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout override func loadView() { super.loadView() + } + + required init(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("refreshItems:"), name: "loadedItems", object: nil) } + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self, name: "loadedItems", object: nil) + } + override func viewDidLoad() { super.viewDidLoad() @@ -68,7 +79,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout layout.sectionInset = UIEdgeInsets(top: 1, left: 0, bottom: 1, right: 0) layout.minimumInteritemSpacing = 0.0; - layout.itemSize = CGSize(width: (15 * 16) - 6, height: 15 * 9) + layout.itemSize = defaultItemSize layout.headerReferenceSize = CGSize(width: 50, height: 50) collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout) @@ -91,16 +102,50 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout lpgr.delaysTouchesBegan = true self.collectionView.addGestureRecognizer(lpgr); + let barbutton = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: Selector("overview")); + navigationItem.rightBarButtonItems = [barbutton] + if detailItem != nil { + println("Details \(detailItem!.children)") + } + } + + func overview() { + let pc = MediaPhotoController() + + var i = [MediaItem]() + + for it in detailItem!.children { + for c in it.children { + i.append(c) + } + } + + pc.items = i + pc.completionHandler = { + self.dismissViewControllerAnimated(true, completion: nil); + } + let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. + + presentViewController(navController, animated: false, completion: nil) } func refreshItems(notification: NSNotification) { - let index = notification.object as! Int + let i = notification.object as! MediaItem + let index = i.index + + let oid = ObjectIdentifier(self) if let detail: MediaItem = self.detailItem { - if (count(detail.children) > 100) { - self.collectionView.reloadData() + if i.parent! !== detail { + return } - else { + + println ("Object: \(oid.hashValue) Index \(index) Item: \(i) Parent: \(i.parent) Detail: \(detailItem)") + +// if (count(detail.children) > 100) { +// self.collectionView.reloadData() +// } +// else { collectionView.performBatchUpdates({ var newItems = [NSIndexPath]() var j = 0 @@ -119,7 +164,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } return }, completion: nil) - } +// } } } @@ -234,6 +279,65 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout self.currentItem = items performSegueWithIdentifier("showVideo", sender: self) } + else { + let len = count(items.root) + let url = NetworkManager.sharedInstance.baseurl + "/service/listfiles" + items.root + "/" + items.path + "/" + items.name + + println(items) + println(url) + + Alamofire.request(.GET, url).responseSwiftyJSON({ + (request, response, json, error) in + + var im = [MediaItem]() + var hashes = Dictionary() + + for (a, c) in json { + // let (a,b) = j # + let s = c.object as! NSString + + if s.hasSuffix(".jpg") { + let l = s.length + let name = (s as NSString).lastPathComponent + var pathlen = l - len - count(name) + +// if (pathlen > 1000) { + println(pathlen) + println(name) + println(s) +// } + if (pathlen < 2) { + pathlen = 2 + } + + let path = (s as NSString).substringWithRange(NSMakeRange(len + 1, pathlen - 2)) + + let folderName = path.lastPathComponent + let fl = count(path) + let pfl = fl - count(folderName) + + println ("\(folderName) \(pfl)") + let fpath = (s as NSString).substringWithRange(NSMakeRange(0, pfl)) + + let i = MediaItem(name: folderName, path: fpath, root: items.root, type: ItemType.PICS) + i.thumbUrl = "\(s)?preview=true" + if !name.hasPrefix(".") { + im.append(i) + } + } + } + + let pc = MediaPhotoController() + + pc.items = im + pc.completionHandler = { + self.dismissViewControllerAnimated(true, completion: nil); + } + let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. + + self.presentViewController(navController, animated: false, completion: nil) + }) + } } } diff --git a/kplayer/detail/ItemCell.swift b/kplayer/detail/ItemCell.swift index 01b407d..5f6ec05 100644 --- a/kplayer/detail/ItemCell.swift +++ b/kplayer/detail/ItemCell.swift @@ -25,6 +25,7 @@ class ItemCell: UICollectionViewCell { super.init(frame: frame) image = UIImageView(frame: frame) + image.contentMode = UIViewContentMode.ScaleAspectFit autolayout(["imag": image], constraints: @@ -35,8 +36,8 @@ class ItemCell: UICollectionViewCell { func setItem(item: MediaItem) { self.item = item - if let url = item.thumbUrl { - image.hnk_setImageFromURL(NSURL(string: item.thumbUrlAbsolute)!, placeholder: defaultImage) + if let url = item.thumbUrl, nsurl = NSURL(string: item.thumbUrlAbsolute) { + image.hnk_setImageFromURL(nsurl, placeholder: defaultImage) } else { if let i = item.image { image.image = i.scaleToSize(15 * 16, height: 15 * 9) diff --git a/kplayer/detail/VideoPlayerController.swift b/kplayer/detail/VideoPlayerController.swift index 5f660f1..cf910ca 100644 --- a/kplayer/detail/VideoPlayerController.swift +++ b/kplayer/detail/VideoPlayerController.swift @@ -29,6 +29,7 @@ class VideoPlayerController: UIViewController { var thumbnailTime: NSTimeInterval = 0.0 var edit = true + var allowEdit = true var index = 0 override func viewDidLoad() { @@ -52,6 +53,10 @@ class VideoPlayerController: UIViewController { } func doEdit(sender: AnyObject) { + if (!allowEdit) { + return + } + if (edit) { edit = false reviewButton!.tintColor = UIColor.blueColor() diff --git a/kplayer/exswift/Array.swift b/kplayer/exswift/Array.swift new file mode 100644 index 0000000..80287d5 --- /dev/null +++ b/kplayer/exswift/Array.swift @@ -0,0 +1,1419 @@ +// +// Array.swift +// ExSwift +// +// Created by pNre on 03/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +internal extension Array { + + private var indexesInterval: HalfOpenInterval { return HalfOpenInterval(0, self.count) } + + /** + Checks if self contains a list of items. + + :param: items Items to search for + :returns: true if self contains all the items + */ + func contains (items: T...) -> Bool { + return items.all { self.indexOf($0) >= 0 } + } + + /** + Difference of self and the input arrays. + + :param: values Arrays to subtract + :returns: Difference of self and the input arrays + */ + func difference (values: [T]...) -> [T] { + + var result = [T]() + + elements: for e in self { + if let element = e as? T { + for value in values { + // if a value is in both self and one of the values arrays + // jump to the next iteration of the outer loop + if value.contains(element) { + continue elements + } + } + + // element it's only in self + result.append(element) + } + } + + return result + + } + + /** + Intersection of self and the input arrays. + + :param: values Arrays to intersect + :returns: Array of unique values contained in all the dictionaries and self + */ + func intersection (values: [U]...) -> Array { + + var result = self + var intersection = Array() + + for (i, value) in enumerate(values) { + + // the intersection is computed by intersecting a couple per loop: + // self n values[0], (self n values[0]) n values[1], ... + if (i > 0) { + result = intersection + intersection = Array() + } + + // find common elements and save them in first set + // to intersect in the next loop + value.each { (item: U) -> Void in + if result.contains(item) { + intersection.append(item as! Element) + } + } + + } + + return intersection + + } + + /** + Union of self and the input arrays. + + :param: values Arrays + :returns: Union array of unique values + */ + func union (values: [U]...) -> Array { + + var result = self + + for array in values { + for value in array { + if !result.contains(value) { + result.append(value as! Element) + } + } + } + + return result + + } + + /** + First element of the array. + + :returns: First element of the array if not empty + */ + @availability(*, unavailable, message="use the 'first' property instead") func first () -> Element? { + return first + } + + /** + Last element of the array. + + :returns: Last element of the array if not empty + */ + @availability(*, unavailable, message="use the 'last' property instead") func last () -> Element? { + return last + } + + /** + First occurrence of item, if found. + + :param: item The item to search for + :returns: Matched item or nil + */ + func find (item: U) -> T? { + if let index = indexOf(item) { + return self[index] + } + + return nil + } + + /** + First item that meets the condition. + + :param: condition A function which returns a boolean if an element satisfies a given condition or not. + :returns: First matched item or nil + */ + func find (condition: Element -> Bool) -> Element? { + return takeFirst(condition) + } + + /** + Index of the first occurrence of item, if found. + + :param: item The item to search for + :returns: Index of the matched item or nil + */ + func indexOf (item: U) -> Int? { + if item is Element { + return Swift.find(unsafeBitCast(self, [U].self), item) + } + + return nil + } + + /** + Index of the first item that meets the condition. + + :param: condition A function which returns a boolean if an element satisfies a given condition or not. + :returns: Index of the first matched item or nil + */ + func indexOf (condition: Element -> Bool) -> Int? { + for (index, element) in enumerate(self) { + if condition(element) { + return index + } + } + + return nil + } + + /** + Gets the index of the last occurrence of item, if found. + + :param: item The item to search for + :returns: Index of the matched item or nil + */ + func lastIndexOf (item: U) -> Int? { + if item is Element { + for (index, value) in enumerate(lazy(self).reverse()) { + if value as! U == item { + return count - 1 - index + } + } + + return nil + } + + return nil + } + + /** + Gets the object at the specified index, if it exists. + + :param: index + :returns: Object at index in self + */ + func get (index: Int) -> Element? { + + return index >= 0 && index < count ? self[index] : nil + + } + + /** + Gets the objects in the specified range. + + :param: range + :returns: Subarray in range + */ + func get (range: Range) -> Array { + + return self[rangeAsArray: range] + + } + + /** + Returns an array of grouped elements, the first of which contains the first elements + of the given arrays, the 2nd contains the 2nd elements of the given arrays, and so on. + + :param: arrays Arrays to zip + :returns: Array of grouped elements + */ + func zip (arrays: Any...) -> [[Any?]] { + + var result = [[Any?]]() + + // Gets the longest sequence + let max = arrays.map { (element: Any) -> Int in + return reflect(element).count + }.max() as Int + + for i in 0.. Any? in + let (_, mirror) = reflect(element)[i] + return mirror.value + }) + + } + + return result + } + + /** + Produces an array of arrays, each containing n elements, each offset by step. + If the final partition is not n elements long it is dropped. + + :param: n The number of elements in each partition. + :param: step The number of elements to progress between each partition. Set to n if not supplied. + :returns: Array partitioned into n element arrays, starting step elements apart. + */ + func partition (var n: Int, var step: Int? = nil) -> [Array] { + var result = [Array]() + + // If no step is supplied move n each step. + if step == nil { + step = n + } + + if step < 1 { step = 1 } // Less than 1 results in an infinite loop. + if n < 1 { n = 0 } // Allow 0 if user wants [[],[],[]] for some reason. + if n > count { return [[]] } + + for i in stride(from: 0, through: count - n, by: step!) { + result += [self[i..<(i + n)]] + } + + return result + } + + /** + Produces an array of arrays, each containing n elements, each offset by step. + + :param: n The number of elements in each partition. + :param: step The number of elements to progress between each partition. Set to n if not supplied. + :param: pad An array of elements to pad the last partition if it is not long enough to + contain n elements. If nil is passed or there are not enough pad elements + the last partition may less than n elements long. + :returns: Array partitioned into n element arrays, starting step elements apart. + */ + func partition (var n: Int, var step: Int? = nil, pad: Array?) -> [Array] { + var result = [Array]() + + // If no step is supplied move n each step. + if step == nil { + step = n + } + + // Less than 1 results in an infinite loop. + if step < 1 { + step = 1 + } + + // Allow 0 if user wants [[],[],[]] for some reason. + if n < 1 { + n = 0 + } + + for i in stride(from: 0, to: count, by: step!) { + var end = i + n + + if end > count { + end = count + } + + result += [self[i.. [Array] { + var result = [Array]() + + // If no step is supplied move n each step. + if step == nil { + step = n + } + + if step < 1 { step = 1 } // Less than 1 results in an infinite loop. + if n < 1 { n = 0 } // Allow 0 if user wants [[],[],[]] for some reason. + + for i in stride(from: 0, to: count, by: step!) { + result += [self[i..<(i + n)]] + } + + return result + } + + /** + Applies cond to each element in array, splitting it each time cond returns a new value. + + :param: cond Function which takes an element and produces an equatable result. + :returns: Array partitioned in order, splitting via results of cond. + */ + func partitionBy (cond: (Element) -> T) -> [Array] { + var result = [Array]() + var lastValue: T? = nil + + for item in self { + let value = cond(item) + + if value == lastValue { + let index: Int = result.count - 1 + result[index] += [item] + } else { + result.append([item]) + lastValue = value + } + } + + return result + } + + /** + Randomly rearranges the elements of self using the Fisher-Yates shuffle + */ + mutating func shuffle () { + + for var i = self.count - 1; i >= 1; i-- { + let j = Int.random(max: i) + swap(&self[i], &self[j]) + } + + } + + /** + Shuffles the values of the array into a new one + + :returns: Shuffled copy of self + */ + func shuffled () -> Array { + var shuffled = self + + shuffled.shuffle() + + return shuffled + } + + /** + Returns a random subarray of given length. + + :param: n Length + :returns: Random subarray of length n + */ + func sample (size n: Int = 1) -> Array { + if n >= count { + return self + } + + let index = Int.random(max: count - n) + return self[index..<(n + index)] + } + + /** + Max value in the current array (if Array.Element implements the Comparable protocol). + + :returns: Max value + */ + func max () -> U { + + return maxElement(map { + return $0 as! U + }) + + } + + /** + Min value in the current array (if Array.Element implements the Comparable protocol). + + :returns: Min value + */ + func min () -> U { + + return minElement(map { + return $0 as! U + }) + + } + + /** + The value for which call(value) is highest. + + :returns: Max value in terms of call(value) + */ + func maxBy (call: (Element) -> (U)) -> Element? { + + if let firstValue = self.first { + var maxElement: T = firstValue + var maxValue: U = call(firstValue) + for i in 1.. maxValue { + maxElement = element + maxValue = value + } + } + return maxElement + } else { + return nil + } + + } + + /** + The value for which call(value) is lowest. + + :returns: Min value in terms of call(value) + */ + func minBy (call: (Element) -> (U)) -> Element? { + + if let firstValue = self.first { + var minElement: T = firstValue + var minValue: U = call(firstValue) + for i in 1.. ()) { + + for item in self { + call(item) + } + + } + + /** + Iterates on each element of the array with its index. + + :param: call Function to call for each element + */ + func each (call: (Int, Element) -> ()) { + + for (index, item) in enumerate(self) { + call(index, item) + } + + } + + /** + Iterates on each element of the array from Right to Left. + + :param: call Function to call for each element + */ + @availability(*, unavailable, message="use 'reverse().each' instead") func eachRight (call: (Element) -> ()) { + reverse().each(call) + } + + /** + Iterates on each element of the array, with its index, from Right to Left. + + :param: call Function to call for each element + */ + @availability(*, unavailable, message="use 'reverse().each' instead") func eachRight (call: (Int, Element) -> ()) { + for (index, item) in enumerate(reverse()) { + call(count - index - 1, item) + } + } + + /** + Checks if test returns true for any element of self. + + :param: test Function to call for each element + :returns: true if test returns true for any element of self + */ + func any (test: (Element) -> Bool) -> Bool { + for item in self { + if test(item) { + return true + } + } + + return false + } + + /** + Checks if test returns true for all the elements in self + + :param: test Function to call for each element + :returns: True if test returns true for all the elements in self + */ + func all (test: (Element) -> Bool) -> Bool { + for item in self { + if !test(item) { + return false + } + } + + return true + } + + /** + Opposite of filter. + + :param: exclude Function invoked to test elements for the exclusion from the array + :returns: Filtered array + */ + func reject (exclude: (Element -> Bool)) -> Array { + return filter { + return !exclude($0) + } + } + + /** + Returns an array containing the first n elements of self. + + :param: n Number of elements to take + :returns: First n elements + */ + func take (n: Int) -> Array { + return self[0.. Bool) -> Array { + + var lastTrue = -1 + + for (index, value) in enumerate(self) { + if condition(value) { + lastTrue = index + } else { + break + } + } + + return take(lastTrue + 1) + + } + + /** + Returns the first element in the array to meet the condition. + + :param: condition A function which returns a boolean if an element satisfies a given condition or not. + :returns: The first element in the array to meet the condition + */ + func takeFirst (condition: (Element) -> Bool) -> Element? { + + for value in self { + if condition(value) { + return value + } + } + + return nil + + } + + /** + Returns an array containing the the last n elements of self. + + :param: n Number of elements to take + :returns: Last n elements + */ + func tail (n: Int) -> Array { + + return self[(count - n).. Array { + + return n > count ? [] : self[Int(n).. Bool) -> Array { + + var lastTrue = -1 + + for (index, value) in enumerate(self) { + if condition(value) { + lastTrue = index + } else { + break + } + } + + return skip(lastTrue + 1) + } + + /** + Costructs an array removing the duplicate values in self + if Array.Element implements the Equatable protocol. + + :returns: Array of unique values + */ + func unique () -> [T] { + var result = [T]() + + for item in self { + if !result.contains(item as! T) { + result.append(item as! T) + } + } + + return result + } + + /** + Returns the set of elements for which call(element) is unique + + :param: call The closure to use to determine uniqueness + :returns: The set of elements for which call(element) is unique + */ + func uniqueBy (call: (Element) -> (T)) -> [Element] { + var result: [Element] = [] + var uniqueItems: [T] = [] + + for item in self { + var callResult: T = call(item) + if !uniqueItems.contains(callResult) { + uniqueItems.append(callResult) + result.append(item) + } + } + + return result + } + + /** + Returns all permutations of a given length within an array + + :param: length The length of each permutation + :returns: All permutations of a given length within an array + */ + func permutation (length: Int) -> [[T]] { + var selfCopy = self + if length < 0 || length > self.count { + return [] + } else if length == 0 { + return [[]] + } else { + var permutations: [[T]] = [] + let combinations = combination(length) + for combination in combinations { + var endArray: [[T]] = [] + var mutableCombination = combination + permutations += self.permutationHelper(length, array: &mutableCombination, endArray: &endArray) + } + return permutations + } + } + + /** + Recursive helper method where all of the permutation-generating work is done + This is Heap's algorithm + */ + private func permutationHelper(n: Int, inout array: [T], inout endArray: [[T]]) -> [[T]] { + if n == 1 { + endArray += [array] + } + for var i = 0; i < n; i++ { + permutationHelper(n - 1, array: &array, endArray: &endArray) + var j = n % 2 == 0 ? i : 0; + //(array[j], array[n - 1]) = (array[n - 1], array[j]) + var temp: T = array[j] + array[j] = array[n - 1] + array[n - 1] = temp + } + return endArray + } + + /** + Creates a dictionary composed of keys generated from the results of + running each element of self through groupingFunction. The corresponding + value of each key is an array of the elements responsible for generating the key. + + :param: groupingFunction + :returns: Grouped dictionary + */ + func groupBy (groupingFunction group: (Element) -> U) -> [U: Array] { + + var result = [U: Array]() + + for item in self { + + let groupKey = group(item) + + // If element has already been added to dictionary, append to it. If not, create one. + if result.has(groupKey) { + result[groupKey]! += [item] + } else { + result[groupKey] = [item] + } + } + + return result + } + + /** + Similar to groupBy, instead of returning a list of values, + returns the number of values for each group. + + :param: groupingFunction + :returns: Grouped dictionary + */ + func countBy (groupingFunction group: (Element) -> U) -> [U: Int] { + + var result = [U: Int]() + + for item in self { + let groupKey = group(item) + + if result.has(groupKey) { + result[groupKey]!++ + } else { + result[groupKey] = 1 + } + } + + return result + } + + /** + Returns all of the combinations in the array of the given length, allowing repeats + + :param: length + :returns: Combinations + */ + func repeatedCombination (length: Int) -> [[Element]] { + if length < 0 { + return [] + } + var indexes: [Int] = [] + length.times { + indexes.append(0) + } + var combinations: [[Element]] = [] + var offset = self.count - indexes.count + while true { + var combination: [Element] = [] + for index in indexes { + combination.append(self[index]) + } + combinations.append(combination) + var i = indexes.count - 1 + while i >= 0 && indexes[i] == self.count - 1 { + i-- + } + if i < 0 { + break + } + indexes[i]++ + (i+1).upTo(indexes.count - 1) { j in + indexes[j] = indexes[i] + } + } + return combinations + } + + /** + Returns all of the combinations in the array of the given length + + :param: length + :returns: Combinations + */ + func combination (length: Int) -> [[Element]] { + if length < 0 || length > self.count { + return [] + } + var indexes: [Int] = (0..= 0 && indexes[i] == i + offset { + i-- + } + if i < 0 { + break + } + i++ + var start = indexes[i-1] + 1 + for j in (i-1).. [[T]] { + if length < 1 { + return [] + } + var permutationIndexes: [[Int]] = [] + permutationIndexes.repeatedPermutationHelper([], length: length, arrayLength: self.count, permutationIndexes: &permutationIndexes) + return permutationIndexes.map({ $0.map({ i in self[i] }) }) + } + + private func repeatedPermutationHelper(seed: [Int], length: Int, arrayLength: Int, inout permutationIndexes: [[Int]]) { + if seed.count == length { + permutationIndexes.append(seed) + return + } + for i in (0.. Bool) -> Int { + + var result = 0 + + for item in self { + if test(item) { + result++ + } + } + + return result + } + + func eachIndex (call: (Int) -> ()) -> () { + (0.. [[T]] { // () -> [[U]] { + var maxWidth: Int = array.map({ $0.count }).max() + var transposition = [[T]](count: maxWidth, repeatedValue: []) + + (0.. i { + transposition[i].append(array[j][i]) + } + } + } + return transposition + } + + /** + Replaces each element in the array with object. I.e., it keeps the length the same but makes the element at every index be object + + :param: object The object to replace each element with + */ + mutating func fill (object: T) -> () { + (0.. (separator: C) -> C? { + if Element.self is C.Type { + return Swift.join(separator, unsafeBitCast(self, [C].self)) + } + + return nil + } + + + /** + Creates an array with values generated by running each value of self + through the mapFunction and discarding nil return values. + + :param: mapFunction + :returns: Mapped array + */ + func mapFilter (mapFunction map: (Element) -> (V)?) -> [V] { + + var mapped = [V]() + + each { (value: Element) -> Void in + if let mappedValue = map(value) { + mapped.append(mappedValue) + } + } + + return mapped + + } + + /** + Creates an array with values and an accumulated result by running accumulated result + and each value of self through the mapFunction. + + :param: initial Initial value for accumulator + :param: mapFunction + :returns: Accumulated value and mapped array + */ + func mapAccum (initial: U, mapFunction map: (U, Element) -> (U, V)) -> (U, [V]) { + var mapped = [V]() + var acc = initial + + each { (value: Element) -> Void in + let (mappedAcc, mappedValue) = map(acc, value) + acc = mappedAcc + mapped.append(mappedValue) + } + + return (acc, mapped) + } + + /** + self.reduce with initial value self.first() + */ + func reduce (combine: (Element, Element) -> Element) -> Element? { + if let firstElement = first { + return skip(1).reduce(firstElement, combine: combine) + } + + return nil + } + + /** + self.reduce from right to left + */ + @availability(*, unavailable, message="use 'reverse().reduce' instead") func reduceRight (initial: U, combine: (U, Element) -> U) -> U { + return reverse().reduce(initial, combine: combine) + } + + /** + self.reduceRight with initial value self.last() + */ + @availability(*, unavailable, message="use 'reverse().reduce' instead") func reduceRight (combine: (Element, Element) -> Element) -> Element? { + return reverse().reduce(combine) + } + + /** + Creates an array with the elements at the specified indexes. + + :param: indexes Indexes of the elements to get + :returns: Array with the elements at indexes + */ + func at (indexes: Int...) -> Array { + return indexes.map { self.get($0)! } + } + + /** + Converts the array to a dictionary with the keys supplied via the keySelector. + + :param: keySelector + :returns: A dictionary + */ + func toDictionary (keySelector:(Element) -> U) -> [U: Element] { + var result: [U: Element] = [:] + for item in self { + result[keySelector(item)] = item + } + + return result + } + + /** + Converts the array to a dictionary with keys and values supplied via the transform function. + + :param: transform + :returns: A dictionary + */ + func toDictionary (transform: (Element) -> (key: K, value: V)?) -> [K: V] { + var result: [K: V] = [:] + for item in self { + if let entry = transform(item) { + result[entry.key] = entry.value + } + } + + return result + } + + /** + Flattens a nested Array self to an array of OutType objects. + + :returns: Flattened array + */ + func flatten () -> [OutType] { + var result = [OutType]() + let reflection = reflect(self) + + for i in 0.. [AnyObject] { + var result = [AnyObject]() + + for item in self { + if let array = item as? NSArray { + result += array.flattenAny() + } else if let object = item as? NSObject { + result.append(object) + } + } + + return result + } + + /** + Sorts the array according to the given comparison function. + + :param: isOrderedBefore Comparison function. + :returns: An array that is sorted according to the given function + */ + @availability(*, unavailable, message="use 'sorted' instead") func sortBy (isOrderedBefore: (T, T) -> Bool) -> [T] { + return sorted(isOrderedBefore) + } + + /** + Calls the passed block for each element in the array, either n times or infinitely, if n isn't specified + + :param: n the number of times to cycle through + :param: block the block to run for each element in each cycle + */ + func cycle (n: Int? = nil, block: (T) -> ()) { + var cyclesRun = 0 + while true { + if let n = n { + if cyclesRun >= n { + break + } + } + for item in self { + block(item) + } + cyclesRun++ + } + } + + /** + Runs a binary search to find the smallest element for which the block evaluates to true + The block should return true for all items in the array above a certain point and false for all items below a certain point + If that point is beyond the furthest item in the array, it returns nil + + See http://ruby-doc.org/core-2.2.0/Array.html#method-i-bsearch regarding find-minimum mode for more + + :param: block the block to run each time + :returns: the min element, or nil if there are no items for which the block returns true + */ + func bSearch (block: (T) -> (Bool)) -> T? { + if count == 0 { + return nil + } + + var low = 0 + var high = count - 1 + while low <= high { + var mid = low + (high - low) / 2 + if block(self[mid]) { + if mid == 0 || !block(self[mid - 1]) { + return self[mid] + } else { + high = mid + } + } else { + low = mid + 1 + } + } + + return nil + } + + /** + Runs a binary search to find some element for which the block returns 0. + The block should return a negative number if the current value is before the target in the array, 0 if it's the target, and a positive number if it's after the target + The Spaceship operator is a perfect fit for this operation, e.g. if you want to find the object with a specific date and name property, you could keep the array sorted by date first, then name, and use this call: + let match = bSearch { [targetDate, targetName] <=> [$0.date, $0.name] } + + See http://ruby-doc.org/core-2.2.0/Array.html#method-i-bsearch regarding find-any mode for more + + :param: block the block to run each time + :returns: an item (there could be multiple matches) for which the block returns true + */ + func bSearch (block: (T) -> (Int)) -> T? { + let match = bSearch { item in + block(item) >= 0 + } + if let match = match { + return block(match) == 0 ? match : nil + } else { + return nil + } + } + + /** + Sorts the array by the value returned from the block, in ascending order + + :param: block the block to use to sort by + :returns: an array sorted by that block, in ascending order + */ + func sortUsing (block: ((T) -> U)) -> [T] { + return self.sorted({ block($0.0) < block($0.1) }) + } + + /** + Removes the last element from self and returns it. + + :returns: The removed element + */ + mutating func pop () -> Element? { + + if self.isEmpty { + return nil + } + + return removeLast() + + } + + /** + Same as append. + + :param: newElement Element to append + */ + mutating func push (newElement: Element) { + return append(newElement) + } + + /** + Returns the first element of self and removes it from the array. + + :returns: The removed element + */ + mutating func shift () -> Element? { + + if self.isEmpty { + return nil + } + + return removeAtIndex(0) + + } + + /** + Prepends an object to the array. + + :param: newElement Object to prepend + */ + mutating func unshift (newElement: Element) { + insert(newElement, atIndex: 0) + } + + /** + Inserts an array at a given index in self. + + :param: newArray Array to insert + :param: atIndex Where to insert the array + */ + mutating func insert (newArray: Array, atIndex: Int) { + self = take(atIndex) + newArray + skip(atIndex) + } + + /** + Deletes all the items in self that are equal to element. + + :param: element Element to remove + */ + mutating func remove (element: U) { + let anotherSelf = self + + removeAll(keepCapacity: true) + + anotherSelf.each { + (index: Int, current: Element) in + if (current as! U) != element { + self.append(current) + } + } + } + + /** + Constructs an array containing the values in the given range. + + :param: range + :returns: Array of values + */ + @availability(*, unavailable, message="use the '[U](range)' constructor") static func range (range: Range) -> [U] { + return [U](range) + } + + /** + Returns the subarray in the given range. + + :param: range Range of the subarray elements + :returns: Subarray or nil if the index is out of bounds + */ + subscript (#rangeAsArray: Range) -> Array { + // Fix out of bounds indexes + let start = Swift.max(0, rangeAsArray.startIndex) + let end = Swift.min(rangeAsArray.endIndex, count) + + if start > end { + return [] + } + + return Array(self[Range(start: start, end: end)] as ArraySlice) + } + + /** + Returns a subarray whose items are in the given interval in self. + + :param: interval Interval of indexes of the subarray elements + :returns: Subarray or nil if the index is out of bounds + */ + subscript (interval: HalfOpenInterval) -> Array { + return self[rangeAsArray: Range(start: interval.start, end: interval.end)] + } + + /** + Returns a subarray whose items are in the given interval in self. + + :param: interval Interval of indexes of the subarray elements + :returns: Subarray or nil if the index is out of bounds + */ + subscript (interval: ClosedInterval) -> Array { + return self[rangeAsArray: Range(start: interval.start, end: interval.end + 1)] + } + + /** + Creates an array with the elements at indexes in the given list of integers. + + :param: first First index + :param: second Second index + :param: rest Rest of indexes + :returns: Array with the items at the specified indexes + */ + subscript (first: Int, second: Int, rest: Int...) -> Array { + let indexes = [first, second] + rest + return indexes.map { self[$0] } + } + +} + +/** + Remove an element from the array +*/ +public func - (first: [T], second: T) -> [T] { + return first - [second] +} + +/** + Difference operator +*/ +public func - (first: [T], second: [T]) -> [T] { + return first.difference(second) +} + +/** + Intersection operator +*/ +public func & (first: [T], second: [T]) -> [T] { + return first.intersection(second) +} + +/** + Union operator +*/ +public func | (first: [T], second: [T]) -> [T] { + return first.union(second) +} +/** + Array duplication. + + :param: array Array to duplicate + :param: n How many times the array must be repeated + :returns: Array of repeated values +*/ +public func * (array: [ItemType], n: Int) -> [ItemType] { + + var result = [ItemType]() + + (0.. String { + return array.implode(separator)! +} diff --git a/kplayer/exswift/Bool.swift b/kplayer/exswift/Bool.swift new file mode 100644 index 0000000..7071efe --- /dev/null +++ b/kplayer/exswift/Bool.swift @@ -0,0 +1,18 @@ +// +// Bool.swift +// ExSwift +// +// Created by Hernandez Alvarez, David on 2/10/15. +// Copyright (c) 2015 pNre. All rights reserved. +// + +import Foundation + +extension Bool { + + mutating func toggle() -> Bool { + self = !self + return self + } + +} \ No newline at end of file diff --git a/kplayer/exswift/Character.swift b/kplayer/exswift/Character.swift new file mode 100644 index 0000000..e3f0c11 --- /dev/null +++ b/kplayer/exswift/Character.swift @@ -0,0 +1,21 @@ +// +// Character.swift +// ExSwift +// +// Created by Cenny Davidsson on 2014-12-08. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +public extension Character { + + /** + If the character represents an integer that fits into an Int, returns + the corresponding integer. + */ + public func toInt () -> Int? { + return String(self).toInt() + } + +} \ No newline at end of file diff --git a/kplayer/exswift/Dictionary.swift b/kplayer/exswift/Dictionary.swift new file mode 100644 index 0000000..671026a --- /dev/null +++ b/kplayer/exswift/Dictionary.swift @@ -0,0 +1,421 @@ +// +// Dictionary.swift +// ExSwift +// +// Created by pNre on 04/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation +import Swift + +internal extension Dictionary { + + /** + Difference of self and the input dictionaries. + Two dictionaries are considered equal if they contain the same [key: value] pairs. + + :param: dictionaries Dictionaries to subtract + :returns: Difference of self and the input dictionaries + */ + func difference (dictionaries: [Key: V]...) -> [Key: V] { + + var result = [Key: V]() + + each { + if let item = $1 as? V { + result[$0] = item + } + } + + // Difference + for dictionary in dictionaries { + for (key, value) in dictionary { + if result.has(key) && result[key] == value { + result.removeValueForKey(key) + } + } + } + + return result + + } + + /** + Union of self and the input dictionaries. + + :param: dictionaries Dictionaries to join + :returns: Union of self and the input dictionaries + */ + func union (dictionaries: Dictionary...) -> Dictionary { + + var result = self + + dictionaries.each { (dictionary) -> Void in + dictionary.each { (key, value) -> Void in + _ = result.updateValue(value, forKey: key) + } + } + + return result + + } + + /** + Intersection of self and the input dictionaries. + Two dictionaries are considered equal if they contain the same [key: value] copules. + + :param: values Dictionaries to intersect + :returns: Dictionary of [key: value] couples contained in all the dictionaries and self + */ + func intersection (dictionaries: [K: V]...) -> [K: V] { + + // Casts self from [Key: Value] to [K: V] + let filtered = mapFilter { (item, value) -> (K, V)? in + if (item is K) && (value is V) { + return (item as! K, value as! V) + } + + return nil + } + + // Intersection + return filtered.filter({ (key: K, value: V) -> Bool in + // check for [key: value] in all the dictionaries + dictionaries.all { $0.has(key) && $0[key] == value } + }) + + } + + /** + Checks if a key exists in the dictionary. + + :param: key Key to check + :returns: true if the key exists + */ + func has (key: Key) -> Bool { + return indexForKey(key) != nil + } + + /** + Creates an Array with values generated by running + each [key: value] of self through the mapFunction. + + :param: mapFunction + :returns: Mapped array + */ + func toArray (map: (Key, Value) -> V) -> [V] { + + var mapped = [V]() + + each { + mapped.append(map($0, $1)) + } + + return mapped + + } + + /** + Creates a Dictionary with the same keys as self and values generated by running + each [key: value] of self through the mapFunction. + + :param: mapFunction + :returns: Mapped dictionary + */ + func mapValues (map: (Key, Value) -> V) -> [Key: V] { + + var mapped = [Key: V]() + + each { + mapped[$0] = map($0, $1) + } + + return mapped + + } + + /** + Creates a Dictionary with the same keys as self and values generated by running + each [key: value] of self through the mapFunction discarding nil return values. + + :param: mapFunction + :returns: Mapped dictionary + */ + func mapFilterValues (map: (Key, Value) -> V?) -> [Key: V] { + + var mapped = [Key: V]() + + each { + if let value = map($0, $1) { + mapped[$0] = value + } + } + + return mapped + + } + + /** + Creates a Dictionary with keys and values generated by running + each [key: value] of self through the mapFunction discarding nil return values. + + :param: mapFunction + :returns: Mapped dictionary + */ + func mapFilter (map: (Key, Value) -> (K, V)?) -> [K: V] { + + var mapped = [K: V]() + + each { + if let value = map($0, $1) { + mapped[value.0] = value.1 + } + } + + return mapped + + } + + /** + Creates a Dictionary with keys and values generated by running + each [key: value] of self through the mapFunction. + + :param: mapFunction + :returns: Mapped dictionary + */ + func map (map: (Key, Value) -> (K, V)) -> [K: V] { + + var mapped = [K: V]() + + self.each({ + let (_key, _value) = map($0, $1) + mapped[_key] = _value + }) + + return mapped + + } + + /** + Loops trough each [key: value] pair in self. + + :param: eachFunction Function to inovke on each loop + */ + func each (each: (Key, Value) -> ()) { + + for (key, value) in self { + each(key, value) + } + + } + + /** + Constructs a dictionary containing every [key: value] pair from self + for which testFunction evaluates to true. + + :param: testFunction Function called to test each key, value + :returns: Filtered dictionary + */ + func filter (test: (Key, Value) -> Bool) -> Dictionary { + + var result = Dictionary() + + for (key, value) in self { + if test(key, value) { + result[key] = value + } + } + + return result + + } + + /** + Creates a dictionary composed of keys generated from the results of + running each element of self through groupingFunction. The corresponding + value of each key is an array of the elements responsible for generating the key. + + :param: groupingFunction + :returns: Grouped dictionary + */ + func groupBy (group: (Key, Value) -> T) -> [T: [Value]] { + + var result = [T: [Value]]() + + for (key, value) in self { + + let groupKey = group(key, value) + + // If element has already been added to dictionary, append to it. If not, create one. + if result.has(groupKey) { + result[groupKey]! += [value] + } else { + result[groupKey] = [value] + } + + } + + return result + } + + /** + Similar to groupBy. Doesn't return a list of values, but the number of values for each group. + + :param: groupingFunction Function called to define the grouping key + :returns: Grouped dictionary + */ + func countBy (group: (Key, Value) -> (T)) -> [T: Int] { + + var result = [T: Int]() + + for (key, value) in self { + + let groupKey = group(key, value) + + // If element has already been added to dictionary, append to it. If not, create one. + if result.has(groupKey) { + result[groupKey]!++ + } else { + result[groupKey] = 1 + } + } + + return result + } + + /** + Checks if test evaluates true for all the elements in self. + + :param: test Function to call for each element + :returns: true if test returns true for all the elements in self + */ + func all (test: (Key, Value) -> (Bool)) -> Bool { + + for (key, value) in self { + if !test(key, value) { + return false + } + } + + return true + + } + + /** + Checks if test evaluates true for any element of self. + + :param: test Function to call for each element + :returns: true if test returns true for any element of self + */ + func any (test: (Key, Value) -> (Bool)) -> Bool { + + for (key, value) in self { + if test(key, value) { + return true + } + } + + return false + + } + + + /** + Returns the number of elements which meet the condition + + :param: test Function to call for each element + :returns: the number of elements meeting the condition + */ + func countWhere (test: (Key, Value) -> (Bool)) -> Int { + + var result = 0 + + for (key, value) in self { + if test(key, value) { + result++ + } + } + + return result + } + + + /** + Recombines the [key: value] couples in self trough combine using initial as initial value. + + :param: initial Initial value + :param: combine Function that reduces the dictionary + :returns: Resulting value + */ + func reduce (initial: U, combine: (U, Element) -> U) -> U { + return Swift.reduce(self, initial, combine) + } + + /** + Returns a copy of self, filtered to only have values for the whitelisted keys. + + :param: keys Whitelisted keys + :returns: Filtered dictionary + */ + func pick (keys: [Key]) -> Dictionary { + return filter { (key: Key, _) -> Bool in + return keys.contains(key) + } + } + + /** + Returns a copy of self, filtered to only have values for the whitelisted keys. + + :param: keys Whitelisted keys + :returns: Filtered dictionary + */ + func pick (keys: Key...) -> Dictionary { + return pick(unsafeBitCast(keys, [Key].self)) + } + + /** + Returns a copy of self, filtered to only have values for the whitelisted keys. + + :param: keys Keys to get + :returns: Dictionary with the given keys + */ + func at (keys: Key...) -> Dictionary { + return pick(keys) + } + + /** + Removes a (key, value) pair from self and returns it as tuple. + If the dictionary is empty returns nil. + + :returns: (key, value) tuple + */ + mutating func shift () -> (Key, Value)? { + if let key = keys.first { + return (key, removeValueForKey(key)!) + } + + return nil + } +} + +/** + Difference operator +*/ +public func - (first: [K: V], second: [K: V]) -> [K: V] { + return first.difference(second) +} + +/** + Intersection operator +*/ +public func & (first: [K: V], second: [K: V]) -> [K: V] { + return first.intersection(second) +} + +/** + Union operator +*/ +public func | (first: [K: V], second: [K: V]) -> [K: V] { + return first.union(second) +} diff --git a/kplayer/exswift/Double.swift b/kplayer/exswift/Double.swift new file mode 100644 index 0000000..9acfaf6 --- /dev/null +++ b/kplayer/exswift/Double.swift @@ -0,0 +1,93 @@ +// +// Double.swift +// ExSwift +// +// Created by pNre on 10/07/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +public extension Double { + + /** + Absolute value. + + :returns: fabs(self) + */ + func abs () -> Double { + return Foundation.fabs(self) + } + + /** + Squared root. + + :returns: sqrt(self) + */ + func sqrt () -> Double { + return Foundation.sqrt(self) + } + + /** + Rounds self to the largest integer <= self. + + :returns: floor(self) + */ + func floor () -> Double { + return Foundation.floor(self) + } + + /** + Rounds self to the smallest integer >= self. + + :returns: ceil(self) + */ + func ceil () -> Double { + return Foundation.ceil(self) + } + + /** + Rounds self to the nearest integer. + + :returns: round(self) + */ + func round () -> Double { + return Foundation.round(self) + } + + /** + Clamps self to a specified range. + + :param: min Lower bound + :param: max Upper bound + :returns: Clamped value + */ + func clamp (min: Double, _ max: Double) -> Double { + return Swift.max(min, Swift.min(max, self)) + } + + /** + Just like round(), except it supports rounding to an arbitrary number, not just 1 + Be careful about rounding errors + + :params: increment the increment to round to + */ + func roundToNearest(increment: Double) -> Double { + let remainder = self % increment + return remainder < increment / 2 ? self - remainder : self - remainder + increment + } + + /** + Random double between min and max (inclusive). + + :params: min + :params: max + :returns: Random number + */ + static func random(min: Double = 0, max: Double) -> Double { + let diff = max - min; + let rand = Double(arc4random() % (UInt32(RAND_MAX) + 1)) + return ((rand / Double(RAND_MAX)) * diff) + min; + } + +} diff --git a/kplayer/exswift/ExSwift.h b/kplayer/exswift/ExSwift.h new file mode 100644 index 0000000..2194cb1 --- /dev/null +++ b/kplayer/exswift/ExSwift.h @@ -0,0 +1,19 @@ +// +// ExSwift.h +// ExSwift +// +// Created by pNre on 07/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +#import + +//! Project version number for ExSwift. +FOUNDATION_EXPORT double ExSwiftVersionNumber; + +//! Project version string for ExSwift. +FOUNDATION_EXPORT const unsigned char ExSwiftVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/kplayer/exswift/ExSwift.swift b/kplayer/exswift/ExSwift.swift new file mode 100644 index 0000000..82c5dc0 --- /dev/null +++ b/kplayer/exswift/ExSwift.swift @@ -0,0 +1,282 @@ +// +// ExSwift.swift +// ExSwift +// +// Created by pNre on 07/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +infix operator =~ {} +infix operator |~ {} +infix operator .. {} +infix operator <=> {} + +public typealias Ex = ExSwift + +public class ExSwift { + + /** + Creates a wrapper that, executes function only after being called n times. + + :param: n No. of times the wrapper has to be called before function is invoked + :param: function Function to wrap + :returns: Wrapper function + */ + public class func after (n: Int, function: (P...) -> T) -> ((P...) -> T?) { + + typealias Function = [P] -> T + + var times = n + + return { + (params: P...) -> T? in + + // Workaround for the now illegal (T...) type. + let adaptedFunction = unsafeBitCast(function, Function.self) + + if times-- <= 0 { + return adaptedFunction(params) + } + + return nil + } + + } + + /** + Creates a wrapper that, executes function only after being called n times + + :param: n No. of times the wrapper has to be called before function is invoked + :param: function Function to wrap + :returns: Wrapper function + */ + public class func after (n: Int, function: Void -> T) -> (Void -> T?) { + func callAfter (args: Any?...) -> T { + return function() + } + + let f = ExSwift.after(n, function: callAfter) + + return { f([nil]) } + } + + /** + Creates a wrapper function that invokes function once. + Repeated calls to the wrapper function will return the value of the first call. + + :param: function Function to wrap + :returns: Wrapper function + */ + public class func once (function: (P...) -> T) -> ((P...) -> T) { + + typealias Function = [P] -> T + + var returnValue: T? = nil + + return { (params: P...) -> T in + + if returnValue != nil { + return returnValue! + } + + let adaptedFunction = unsafeBitCast(function, Function.self) + returnValue = adaptedFunction(params) + + return returnValue! + + } + + } + + /** + Creates a wrapper function that invokes function once. + Repeated calls to the wrapper function will return the value of the first call. + + :param: function Function to wrap + :returns: Wrapper function + */ + public class func once (function: Void -> T) -> (Void -> T) { + let f = ExSwift.once { + (params: Any?...) -> T in + return function() + } + + return { f([nil]) } + } + + /** + Creates a wrapper that, when called, invokes function with any additional + partial arguments prepended to those provided to the new function. + + :param: function Function to wrap + :param: parameters Arguments to prepend + :returns: Wrapper function + */ + public class func partial (function: (P...) -> T, _ parameters: P...) -> ((P...) -> T) { + typealias Function = [P] -> T + + return { (params: P...) -> T in + let adaptedFunction = unsafeBitCast(function, Function.self) + return adaptedFunction(parameters + params) + } + } + + /** + Creates a wrapper (without any parameter) that, when called, invokes function + automatically passing parameters as arguments. + + :param: function Function to wrap + :param: parameters Arguments to pass to function + :returns: Wrapper function + */ + public class func bind (function: (P...) -> T, _ parameters: P...) -> (Void -> T) { + typealias Function = [P] -> T + + return { Void -> T in + let adaptedFunction = unsafeBitCast(function, Function.self) + return adaptedFunction(parameters) + } + } + + /** + Creates a wrapper for function that caches the result of function's invocations. + + :param: function Function with one parameter to cache + :returns: Wrapper function + */ + public class func cached (function: P -> R) -> (P -> R) { + var cache = [P:R]() + + return { (param: P) -> R in + let key = param + + if let cachedValue = cache[key] { + return cachedValue + } else { + let value = function(param) + cache[key] = value + return value + } + } + } + + /** + Creates a wrapper for function that caches the result of function's invocations. + + :param: function Function to cache + :param: hash Parameters based hashing function that computes the key used to store each result in the cache + :returns: Wrapper function + */ + public class func cached (function: (P...) -> R, hash: ((P...) -> P)) -> ((P...) -> R) { + typealias Function = [P] -> R + typealias Hash = [P] -> P + + var cache = [P:R]() + + return { (params: P...) -> R in + let adaptedFunction = unsafeBitCast(function, Function.self) + let adaptedHash = unsafeBitCast(hash, Hash.self) + + let key = adaptedHash(params) + + if let cachedValue = cache[key] { + return cachedValue + } else { + let value = adaptedFunction(params) + cache[key] = value + return value + } + } + } + + /** + Creates a wrapper for function that caches the result of function's invocations. + + :param: function Function to cache + :returns: Wrapper function + */ + public class func cached (function: (P...) -> R) -> ((P...) -> R) { + return cached(function, hash: { (params: P...) -> P in return params[0] }) + } + + /** + Utility method to return an NSRegularExpression object given a pattern. + + :param: pattern Regex pattern + :param: ignoreCase If true the NSRegularExpression is created with the NSRegularExpressionOptions.CaseInsensitive flag + :returns: NSRegularExpression object + */ + internal class func regex (pattern: String, ignoreCase: Bool = false) -> NSRegularExpression? { + + var options = NSRegularExpressionOptions.DotMatchesLineSeparators.rawValue + + if ignoreCase { + options = NSRegularExpressionOptions.CaseInsensitive.rawValue | options + } + + var error: NSError? = nil + let regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: options), error: &error) + + return (error == nil) ? regex : nil + + } + +} + +func <=> (lhs: T, rhs: T) -> Int { + if lhs < rhs { + return -1 + } else if lhs > rhs { + return 1 + } else { + return 0 + } +} + +/** +* Internal methods +*/ +extension ExSwift { + + /** + * Converts, if possible, and flattens an object from its Objective-C + * representation to the Swift one. + * @param object Object to convert + * @returns Flattenend array of converted values + */ + internal class func bridgeObjCObject (object: S) -> [T] { + var result = [T]() + let reflection = reflect(object) + + // object has an Objective-C type + if let obj = object as? T { + // object has type T + result.append(obj) + } else if reflection.disposition == .ObjCObject { + + var bridgedValue: T!? + + // If it is an NSArray, flattening will produce the expected result + if let array = object as? NSArray { + result += array.flatten() + } else if let bridged = reflection.value as? T { + result.append(bridged) + } + } else if reflection.disposition == .IndexContainer { + // object is a native Swift array + + // recursively convert each item + (0.. Float { + return fabsf(self) + } + + /** + Squared root. + + :returns: sqrtf(self) + */ + func sqrt () -> Float { + return sqrtf(self) + } + + /** + Rounds self to the largest integer <= self. + + :returns: floorf(self) + */ + func floor () -> Float { + return floorf(self) + } + + /** + Rounds self to the smallest integer >= self. + + :returns: ceilf(self) + */ + func ceil () -> Float { + return ceilf(self) + } + + /** + Rounds self to the nearest integer. + + :returns: roundf(self) + */ + func round () -> Float { + return roundf(self) + } + + /** + Clamps self to a specified range. + + :param: min Lower bound + :param: max Upper bound + :returns: Clamped value + */ + func clamp (min: Float, _ max: Float) -> Float { + return Swift.max(min, Swift.min(max, self)) + } + + /** + Random float between min and max (inclusive). + + :param: min + :param: max + :returns: Random number + */ + static func random(min: Float = 0, max: Float) -> Float { + let diff = max - min; + let rand = Float(arc4random() % (UInt32(RAND_MAX) + 1)) + return ((rand / Float(RAND_MAX)) * diff) + min; + } + +} + diff --git a/kplayer/exswift/Int.swift b/kplayer/exswift/Int.swift new file mode 100644 index 0000000..26d3ddd --- /dev/null +++ b/kplayer/exswift/Int.swift @@ -0,0 +1,256 @@ +// +// Int.swift +// ExSwift +// +// Created by pNre on 03/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +public extension Int { + + /** + Calls function self times. + + :param: function Function to call + */ + func times (function: Void -> T) { + (0.. Void) { + (0.. (function: (Int) -> T) { + (0.. Bool { + return (self % 2) == 0 + } + + /** + Checks if a number is odd. + + :returns: true if self is odd + */ + func isOdd () -> Bool { + return !isEven() + } + + /** + Iterates function, passing in integer values from self up to and including limit. + + :param: limit Last value to pass + :param: function Function to invoke + */ + func upTo (limit: Int, function: (Int) -> ()) { + if limit < self { + return + } + + (self...limit).each(function) + } + + /** + Iterates function, passing in integer values from self down to and including limit. + + :param: limit Last value to pass + :param: function Function to invoke + */ + func downTo (limit: Int, function: (Int) -> ()) { + if limit > self { + return + } + + Array(limit...self).reverse().each(function) + } + + /** + Clamps self to a specified range. + + :param: range Clamping range + :returns: Clamped value + */ + func clamp (range: Range) -> Int { + return clamp(range.startIndex, range.endIndex - 1) + } + + /** + Clamps self to a specified range. + + :param: min Lower bound + :param: max Upper bound + :returns: Clamped value + */ + func clamp (min: Int, _ max: Int) -> Int { + return Swift.max(min, Swift.min(max, self)) + } + + /** + Checks if self is included a specified range. + + :param: range Range + :param: strict If true, "<" is used for comparison + :returns: true if in range + */ + func isIn (range: Range, strict: Bool = false) -> Bool { + if strict { + return range.startIndex < self && self < range.endIndex - 1 + } + + return range.startIndex <= self && self <= range.endIndex - 1 + } + + /** + Checks if self is included in a closed interval. + + :param: interval Interval to check + :returns: true if in the interval + */ + func isIn (interval: ClosedInterval) -> Bool { + return interval.contains(self) + } + + /** + Checks if self is included in an half open interval. + + :param: interval Interval to check + :returns: true if in the interval + */ + func isIn (interval: HalfOpenInterval) -> Bool { + return interval.contains(self) + } + + /** + Returns an [Int] containing the digits in self. + + :return: Array of digits + */ + func digits () -> [Int] { + var result = [Int]() + + for char in String(self) { + let string = String(char) + if let toInt = string.toInt() { + result.append(toInt) + } + } + + return result + } + + /** + Absolute value. + + :returns: abs(self) + */ + func abs () -> Int { + return Swift.abs(self) + } + + /** + Greatest common divisor of self and n. + + :param: n + :returns: GCD + */ + func gcd (n: Int) -> Int { + return n == 0 ? self : n.gcd(self % n) + } + + /** + Least common multiple of self and n + + :param: n + :returns: LCM + */ + func lcm (n: Int) -> Int { + return (self * n).abs() / gcd(n) + } + + /** + Computes the factorial of self + + :returns: Factorial + */ + func factorial () -> Int { + return self == 0 ? 1 : self * (self - 1).factorial() + } + + /** + Random integer between min and max (inclusive). + + :param: min Minimum value to return + :param: max Maximum value to return + :returns: Random integer + */ + static func random(min: Int = 0, max: Int) -> Int { + return Int(arc4random_uniform(UInt32((max - min) + 1))) + min + } + +} + +/** + NSTimeInterval conversion extensions +*/ +public extension Int { + + var years: NSTimeInterval { + return 365 * self.days + } + + var year: NSTimeInterval { + return self.years + } + + var days: NSTimeInterval { + return 24 * self.hours + } + + var day: NSTimeInterval { + return self.days + } + + var hours: NSTimeInterval { + return 60 * self.minutes + } + + var hour: NSTimeInterval { + return self.hours + } + + var minutes: NSTimeInterval { + return 60 * self.seconds + } + + var minute: NSTimeInterval { + return self.minutes + } + + var seconds: NSTimeInterval { + return NSTimeInterval(self) + } + + var second: NSTimeInterval { + return self.seconds + } + +} diff --git a/kplayer/exswift/NSArray.swift b/kplayer/exswift/NSArray.swift new file mode 100644 index 0000000..d2c3a9e --- /dev/null +++ b/kplayer/exswift/NSArray.swift @@ -0,0 +1,64 @@ +// +// NSArray.swift +// ExSwift +// +// Created by pNre on 10/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +public extension NSArray { + + /** + Converts an NSArray object to an OutType[] array containing the items in the NSArray of type OutType. + + :returns: Array of Swift objects + */ + func cast () -> [OutType] { + var result = [OutType]() + + for item : AnyObject in self { + result += Ex.bridgeObjCObject(item) as [OutType] + } + + return result + } + + /** + Flattens a multidimensional NSArray to an OutType[] array + containing the items in the NSArray that can be bridged from their ObjC type to OutType. + + :returns: Flattened array + */ + func flatten () -> [OutType] { + var result = [OutType]() + let reflection = reflect(self) + + for i in 0.. [AnyObject] { + var result = [AnyObject]() + + for item in self { + if let array = item as? NSArray { + result += array.flattenAny() + } else { + result.append(item) + } + } + + return result + } + +} diff --git a/kplayer/exswift/NSDate.swift b/kplayer/exswift/NSDate.swift new file mode 100644 index 0000000..50d758e --- /dev/null +++ b/kplayer/exswift/NSDate.swift @@ -0,0 +1,318 @@ +// +// File.swift +// ExSwift +// +// Created by Piergiuseppe Longo on 23/11/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +public extension NSDate { + + // MARK: NSDate Manipulation + + /** + Returns a new NSDate object representing the date calculated by adding the amount specified to self date + + :param: seconds number of seconds to add + :param: minutes number of minutes to add + :param: hours number of hours to add + :param: days number of days to add + :param: weeks number of weeks to add + :param: months number of months to add + :param: years number of years to add + :returns: the NSDate computed + */ + public func add(seconds: Int = 0, minutes: Int = 0, hours: Int = 0, days: Int = 0, weeks: Int = 0, months: Int = 0, years: Int = 0) -> NSDate { + var calendar = NSCalendar.currentCalendar() + + let version = floor(NSFoundationVersionNumber) + + if version <= NSFoundationVersionNumber10_9_2 { + var component = NSDateComponents() + component.setValue(seconds, forComponent: .CalendarUnitSecond) + + var date : NSDate! = calendar.dateByAddingComponents(component, toDate: self, options: nil)! + component = NSDateComponents() + component.setValue(minutes, forComponent: .CalendarUnitMinute) + date = calendar.dateByAddingComponents(component, toDate: date, options: nil)! + + component = NSDateComponents() + component.setValue(hours, forComponent: .CalendarUnitHour) + date = calendar.dateByAddingComponents(component, toDate: date, options: nil)! + + component = NSDateComponents() + component.setValue(days, forComponent: .CalendarUnitDay) + date = calendar.dateByAddingComponents(component, toDate: date, options: nil)! + + component = NSDateComponents() + component.setValue(weeks, forComponent: .CalendarUnitWeekOfMonth) + date = calendar.dateByAddingComponents(component, toDate: date, options: nil)! + + component = NSDateComponents() + component.setValue(months, forComponent: .CalendarUnitMonth) + date = calendar.dateByAddingComponents(component, toDate: date, options: nil)! + + component = NSDateComponents() + component.setValue(years, forComponent: .CalendarUnitYear) + date = calendar.dateByAddingComponents(component, toDate: date, options: nil)! + return date + } + + var date : NSDate! = calendar.dateByAddingUnit(.CalendarUnitSecond, value: seconds, toDate: self, options: nil) + date = calendar.dateByAddingUnit(.CalendarUnitMinute, value: minutes, toDate: date, options: nil) + date = calendar.dateByAddingUnit(.CalendarUnitDay, value: days, toDate: date, options: nil) + date = calendar.dateByAddingUnit(.CalendarUnitHour, value: hours, toDate: date, options: nil) + date = calendar.dateByAddingUnit(.CalendarUnitWeekOfMonth, value: weeks, toDate: date, options: nil) + date = calendar.dateByAddingUnit(.CalendarUnitMonth, value: months, toDate: date, options: nil) + date = calendar.dateByAddingUnit(.CalendarUnitYear, value: years, toDate: date, options: nil) + return date + } + + /** + Returns a new NSDate object representing the date calculated by adding an amount of seconds to self date + + :param: seconds number of seconds to add + :returns: the NSDate computed + */ + public func addSeconds (seconds: Int) -> NSDate { + return add(seconds: seconds) + } + + /** + Returns a new NSDate object representing the date calculated by adding an amount of minutes to self date + + :param: minutes number of minutes to add + :returns: the NSDate computed + */ + public func addMinutes (minutes: Int) -> NSDate { + return add(minutes: minutes) + } + + /** + Returns a new NSDate object representing the date calculated by adding an amount of hours to self date + + :param: hours number of hours to add + :returns: the NSDate computed + */ + public func addHours(hours: Int) -> NSDate { + return add(hours: hours) + } + + /** + Returns a new NSDate object representing the date calculated by adding an amount of days to self date + + :param: days number of days to add + :returns: the NSDate computed + */ + public func addDays(days: Int) -> NSDate { + return add(days: days) + } + + /** + Returns a new NSDate object representing the date calculated by adding an amount of weeks to self date + + :param: weeks number of weeks to add + :returns: the NSDate computed + */ + public func addWeeks(weeks: Int) -> NSDate { + return add(weeks: weeks) + } + + + /** + Returns a new NSDate object representing the date calculated by adding an amount of months to self date + + :param: months number of months to add + :returns: the NSDate computed + */ + + public func addMonths(months: Int) -> NSDate { + return add(months: months) + } + + /** + Returns a new NSDate object representing the date calculated by adding an amount of years to self date + + :param: years number of year to add + :returns: the NSDate computed + */ + public func addYears(years: Int) -> NSDate { + return add(years: years) + } + + // MARK: Date comparison + + /** + Checks if self is after input NSDate + + :param: date NSDate to compare + :returns: True if self is after the input NSDate, false otherwise + */ + public func isAfter(date: NSDate) -> Bool{ + return (self.compare(date) == NSComparisonResult.OrderedDescending) + } + + /** + Checks if self is before input NSDate + + :param: date NSDate to compare + :returns: True if self is before the input NSDate, false otherwise + */ + public func isBefore(date: NSDate) -> Bool{ + return (self.compare(date) == NSComparisonResult.OrderedAscending) + } + + + // MARK: Getter + + /** + Date year + */ + public var year : Int { + get { + return getComponent(.CalendarUnitYear) + } + } + + /** + Date month + */ + public var month : Int { + get { + return getComponent(.CalendarUnitMonth) + } + } + + /** + Date weekday + */ + public var weekday : Int { + get { + return getComponent(.CalendarUnitWeekday) + } + } + + /** + Date weekMonth + */ + public var weekMonth : Int { + get { + return getComponent(.CalendarUnitWeekOfMonth) + } + } + + + /** + Date days + */ + public var days : Int { + get { + return getComponent(.CalendarUnitDay) + } + } + + /** + Date hours + */ + public var hours : Int { + + get { + return getComponent(.CalendarUnitHour) + } + } + + /** + Date minuts + */ + public var minutes : Int { + get { + return getComponent(.CalendarUnitMinute) + } + } + + /** + Date seconds + */ + public var seconds : Int { + get { + return getComponent(.CalendarUnitSecond) + } + } + + /** + Returns the value of the NSDate component + + :param: component NSCalendarUnit + :returns: the value of the component + */ + + public func getComponent (component : NSCalendarUnit) -> Int { + let calendar = NSCalendar.currentCalendar() + let components = calendar.components(component, fromDate: self) + + return components.valueForComponent(component) + } +} + +extension NSDate: Strideable { + public func distanceTo(other: NSDate) -> NSTimeInterval { + return other - self + } + + public func advancedBy(n: NSTimeInterval) -> Self { + return self.dynamicType(timeIntervalSinceReferenceDate: self.timeIntervalSinceReferenceDate + n) + } +} +// MARK: Arithmetic + +func +(date: NSDate, timeInterval: Int) -> NSDate { + return date + NSTimeInterval(timeInterval) +} + +func -(date: NSDate, timeInterval: Int) -> NSDate { + return date - NSTimeInterval(timeInterval) +} + +func +=(inout date: NSDate, timeInterval: Int) { + date = date + timeInterval +} + +func -=(inout date: NSDate, timeInterval: Int) { + date = date - timeInterval +} + +func +(date: NSDate, timeInterval: Double) -> NSDate { + return date.dateByAddingTimeInterval(NSTimeInterval(timeInterval)) +} + +func -(date: NSDate, timeInterval: Double) -> NSDate { + return date.dateByAddingTimeInterval(NSTimeInterval(-timeInterval)) +} + +func +=(inout date: NSDate, timeInterval: Double) { + date = date + timeInterval +} + +func -=(inout date: NSDate, timeInterval: Double) { + date = date - timeInterval +} + +func -(date: NSDate, otherDate: NSDate) -> NSTimeInterval { + return date.timeIntervalSinceDate(otherDate) +} + +extension NSDate: Equatable { +} + +public func ==(lhs: NSDate, rhs: NSDate) -> Bool { + return lhs.compare(rhs) == NSComparisonResult.OrderedSame +} + +extension NSDate: Comparable { +} + +public func <(lhs: NSDate, rhs: NSDate) -> Bool { + return lhs.compare(rhs) == NSComparisonResult.OrderedAscending +} diff --git a/kplayer/exswift/Range.swift b/kplayer/exswift/Range.swift new file mode 100644 index 0000000..051368d --- /dev/null +++ b/kplayer/exswift/Range.swift @@ -0,0 +1,80 @@ +// +// Range.swift +// ExSwift +// +// Created by pNre on 04/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +internal extension Range { + + /** + For each element in the range invokes function. + + :param: function Function to call + */ + func times (function: () -> ()) { + each { (current: T) -> () in + function() + } + } + + /** + For each element in the range invokes function passing the element as argument. + + :param: function Function to invoke + */ + func times (function: (T) -> ()) { + each (function) + } + + /** + For each element in the range invokes function passing the element as argument. + + :param: function Function to invoke + */ + func each (function: (T) -> ()) { + for i in self { + function(i) + } + } + + /** + Returns each element of the range in an array + + :returns: Each element of the range in an array + */ + func toArray () -> [T] { + var result: [T] = [] + for i in self { + result.append(i) + } + return result + } + + /** + Range of Int with random bounds between from and to (inclusive). + + :param: from Lower bound + :param: to Upper bound + :returns: Random range + */ + static func random (from: Int, to: Int) -> Range { + let lowerBound = Int.random(min: from, max: to) + let upperBound = Int.random(min: lowerBound, max: to) + + return lowerBound...upperBound + } + +} + +/** +* Operator == to compare 2 ranges first, second by start & end indexes. If first.startIndex is equal to +* second.startIndex and first.endIndex is equal to second.endIndex the ranges are considered equal. +*/ +public func == (first: Range, second: Range) -> Bool { + return first.startIndex == second.startIndex && + first.endIndex == second.endIndex +} diff --git a/kplayer/exswift/Sequence.swift b/kplayer/exswift/Sequence.swift new file mode 100644 index 0000000..6aa19b3 --- /dev/null +++ b/kplayer/exswift/Sequence.swift @@ -0,0 +1,244 @@ +// +// Sequence.swift +// ExSwift +// +// Created by Colin Eberhardt on 24/06/2014. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +internal extension SequenceOf { + + /** + First element of the sequence. + + :returns: First element of the sequence if present + */ + var first: T? { + var generator = self.generate() + return generator.next() + } + + /** + Checks if call returns true for any element of self. + + :param: call Function to call for each element + :returns: True if call returns true for any element of self + */ + func any (call: (T) -> Bool) -> Bool { + var generator = self.generate() + while let nextItem = generator.next() { + if call(nextItem) { + return true + } + } + return false + } + + /** + Object at the specified index if exists. + + :param: index + :returns: Object at index in sequence, nil if index is out of bounds + */ + func get (index: Int) -> T? { + var generator = self.generate() + for _ in 0..<(index - 1) { + generator.next() + } + return generator.next() + } + + /** + Objects in the specified range. + + :param: range + :returns: Subsequence in range + */ + func get (range: Range) -> SequenceOf { + return self.skip(range.startIndex).take(range.endIndex - range.startIndex) + } + + /** + Index of the first occurrence of item, if found. + + :param: item The item to search for + :returns: Index of the matched item or nil + */ + func indexOf (item: U) -> Int? { + var index = 0 + for current in self { + if let equatable = current as? U { + if equatable == item { + return index + } + } + index++ + } + return nil + } + + /** + Subsequence from n to the end of the sequence. + + :param: n Number of elements to skip + :returns: Sequence from n to the end + */ + func skip (n: Int) -> SequenceOf { + var generator = self.generate() + for _ in 0.. Bool) -> SequenceOf { + return SequenceOf(lazy(self).filter(include)) + } + + /** + Opposite of filter. + + :param: exclude Function invoked to test elements for exlcusion from the sequence + :returns: Filtered sequence + */ + func reject (exclude: (T -> Bool)) -> SequenceOf { + return self.filter { + return !exclude($0) + } + } + + /** + Skips the elements in the sequence up until the condition returns false. + + :param: condition A function which returns a boolean if an element satisfies a given condition or not + :returns: Elements of the sequence starting with the element which does not meet the condition + */ + func skipWhile(condition:(T) -> Bool) -> SequenceOf { + var generator = self.generate() + var checkingGenerator = self.generate() + + var keepSkipping = true + + while keepSkipping { + var nextItem = checkingGenerator.next() + keepSkipping = nextItem != nil ? condition(nextItem!) : false + + if keepSkipping { + generator.next() + } + } + return SequenceOf(generator) + } + + /** + Checks if self contains the item object. + + :param: item The item to search for + :returns: true if self contains item + */ + func contains (item: T) -> Bool { + var generator = self.generate() + while let nextItem = generator.next() { + if nextItem as! T == item { + return true + } + } + return false + } + + /** + Returns the first n elements from self. + + :param: n Number of elements to take + :returns: First n elements + */ + func take (n: Int) -> SequenceOf { + return SequenceOf(TakeSequence(self, n)) + } + + /** + Returns the elements of the sequence up until an element does not meet the condition. + + :param: condition A function which returns a boolean if an element satisfies a given condition or not. + :returns: Elements of the sequence up until an element does not meet the condition + */ + func takeWhile (condition:(T?) -> Bool) -> SequenceOf { + return SequenceOf(TakeWhileSequence(self, condition)) + } + + /** + Returns each element of the sequence in an array + + :returns: Each element of the sequence in an array + */ + func toArray () -> [T] { + var result: [T] = [] + for item in self { + result.append(item) + } + return result + } +} + +/** + A sequence adapter that implements the 'take' functionality +*/ +public struct TakeSequence: SequenceType { + private let sequence: S + private let n: Int + + public init(_ sequence: S, _ n: Int) { + self.sequence = sequence + self.n = n + } + + public func generate() -> GeneratorOf { + var count = 0 + var generator = self.sequence.generate() + return GeneratorOf { + count++ + if count > self.n { + return nil + } else { + return generator.next() + } + } + } +} + +/** + a sequence adapter that implements the 'takeWhile' functionality +*/ +public struct TakeWhileSequence: SequenceType { + private let sequence: S + private let condition: (S.Generator.Element?) -> Bool + + public init(_ sequence:S, _ condition:(S.Generator.Element?) -> Bool) { + self.sequence = sequence + self.condition = condition + } + + public func generate() -> GeneratorOf { + var generator = self.sequence.generate() + var endConditionMet = false + return GeneratorOf { + let next: S.Generator.Element? = generator.next() + if !endConditionMet { + endConditionMet = !self.condition(next) + } + if endConditionMet { + return nil + } else { + return next + } + } + } +} diff --git a/kplayer/exswift/String.swift b/kplayer/exswift/String.swift new file mode 100644 index 0000000..95ff794 --- /dev/null +++ b/kplayer/exswift/String.swift @@ -0,0 +1,403 @@ +// +// String.swift +// ExSwift +// +// Created by pNre on 03/06/14. +// Copyright (c) 2014 pNre. All rights reserved. +// + +import Foundation + +public extension String { + + /** + String length + */ + var length: Int { return count(self) } + + /** + self.capitalizedString shorthand + */ + var capitalized: String { return capitalizedString } + + /** + Returns the substring in the given range + + :param: range + :returns: Substring in range + */ + subscript (range: Range) -> String? { + if range.startIndex < 0 || range.endIndex > self.length { + return nil + } + + let range = Range(start: advance(startIndex, range.startIndex), end: advance(startIndex, range.endIndex)) + + return self[range] + } + + /** + Equivalent to at. Takes a list of indexes and returns an Array + containing the elements at the given indexes in self. + + :param: firstIndex + :param: secondIndex + :param: restOfIndexes + :returns: Charaters at the specified indexes (converted to String) + */ + subscript (firstIndex: Int, secondIndex: Int, restOfIndexes: Int...) -> [String] { + return at([firstIndex, secondIndex] + restOfIndexes) + } + + /** + Gets the character at the specified index as String. + If index is negative it is assumed to be relative to the end of the String. + + :param: index Position of the character to get + :returns: Character as String or nil if the index is out of bounds + */ + subscript (index: Int) -> String? { + if let char = Array(self).get(index) { + return String(char) + } + + return nil + } + + /** + Takes a list of indexes and returns an Array containing the elements at the given indexes in self. + + :param: indexes Positions of the elements to get + :returns: Array of characters (as String) + */ + func at (indexes: Int...) -> [String] { + return indexes.map { self[$0]! } + } + + /** + Takes a list of indexes and returns an Array containing the elements at the given indexes in self. + + :param: indexes Positions of the elements to get + :returns: Array of characters (as String) + */ + func at (indexes: [Int]) -> [String] { + return indexes.map { self[$0]! } + } + + /** + Returns an array of strings, each of which is a substring of self formed by splitting it on separator. + + :param: separator Character used to split the string + :returns: Array of substrings + */ + func explode (separator: Character) -> [String] { + return split(self, isSeparator: { (element: Character) -> Bool in + return element == separator + }) + } + + /** + Finds any match in self for pattern. + + :param: pattern Pattern to match + :param: ignoreCase true for case insensitive matching + :returns: Matches found (as [NSTextCheckingResult]) + */ + func matches (pattern: String, ignoreCase: Bool = false) -> [NSTextCheckingResult]? { + + if let regex = ExSwift.regex(pattern, ignoreCase: ignoreCase) { + // Using map to prevent a possible bug in the compiler + return regex.matchesInString(self, options: nil, range: NSMakeRange(0, length)).map { $0 as! NSTextCheckingResult } + } + + return nil + } + + /** + Check is string with this pattern included in string + + :param: pattern Pattern to match + :param: ignoreCase true for case insensitive matching + :returns: true if contains match, otherwise false + */ + func containsMatch (pattern: String, ignoreCase: Bool = false) -> Bool? { + if let regex = ExSwift.regex(pattern, ignoreCase: ignoreCase) { + let range = NSMakeRange(0, count(self)) + return regex.firstMatchInString(self, options: .allZeros, range: range) != nil + } + + return nil + } + + /** + Replace all pattern matches with another string + + :param: pattern Pattern to match + :param: replacementString string to replace matches + :param: ignoreCase true for case insensitive matching + :returns: true if contains match, otherwise false + */ + func replaceMatches (pattern: String, withString replacementString: String, ignoreCase: Bool = false) -> String? { + if let regex = ExSwift.regex(pattern, ignoreCase: ignoreCase) { + let range = NSMakeRange(0, count(self)) + return regex.stringByReplacingMatchesInString(self, options: .allZeros, range: range, withTemplate: replacementString) + } + + return nil + } + + /** + Inserts a substring at the given index in self. + + :param: index Where the new string is inserted + :param: string String to insert + :returns: String formed from self inserting string at index + */ + func insert (var index: Int, _ string: String) -> String { + // Edge cases, prepend and append + if index > length { + return self + string + } else if index < 0 { + return string + self + } + + return self[0.. String { + if let range = rangeOfCharacterFromSet(set.invertedSet) { + return self[range.startIndex.. String { + return trimmedLeft(characterSet: set) + } + + /** + Strips the specified characters from the end of self. + + :returns: Stripped string + */ + func trimmedRight (characterSet set: NSCharacterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet()) -> String { + if let range = rangeOfCharacterFromSet(set.invertedSet, options: NSStringCompareOptions.BackwardsSearch) { + return self[startIndex.. String { + return trimmedRight(characterSet: set) + } + + /** + Strips whitespaces from both the beginning and the end of self. + + :returns: Stripped string + */ + func trimmed () -> String { + return trimmedLeft().trimmedRight() + } + + /** + Costructs a string using random chars from a given set. + + :param: length String length. If < 1, it's randomly selected in the range 0..16 + :param: charset Chars to use in the random string + :returns: Random string + */ + static func random (var length len: Int = 0, charset: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") -> String { + + if len < 1 { + len = Int.random(max: 16) + } + + var result = String() + let max = charset.length - 1 + + len.times { + result += charset[Int.random(min: 0, max: max)]! + } + + return result + + } + + + /** + Parses a string containing a double numerical value into an optional double if the string is a well formed number. + + :returns: A double parsed from the string or nil if it cannot be parsed. + */ + func toDouble() -> Double? { + + let scanner = NSScanner(string: self) + var double: Double = 0 + + if scanner.scanDouble(&double) { + return double + } + + return nil + + } + + /** + Parses a string containing a float numerical value into an optional float if the string is a well formed number. + + :returns: A float parsed from the string or nil if it cannot be parsed. + */ + func toFloat() -> Float? { + + let scanner = NSScanner(string: self) + var float: Float = 0 + + if scanner.scanFloat(&float) { + return float + } + + return nil + + } + + /** + Parses a string containing a non-negative integer value into an optional UInt if the string is a well formed number. + + :returns: A UInt parsed from the string or nil if it cannot be parsed. + */ + func toUInt() -> UInt? { + if let val = self.trimmed().toInt() { + if val < 0 { + return nil + } + return UInt(val) + } + + return nil + } + + + /** + Parses a string containing a boolean value (true or false) into an optional Bool if the string is a well formed. + + :returns: A Bool parsed from the string or nil if it cannot be parsed as a boolean. + */ + func toBool() -> Bool? { + let text = self.trimmed().lowercaseString + if text == "true" || text == "false" || text == "yes" || text == "no" { + return (text as NSString).boolValue + } + + return nil + } + + /** + Parses a string containing a date into an optional NSDate if the string is a well formed. + The default format is yyyy-MM-dd, but can be overriden. + + :returns: A NSDate parsed from the string or nil if it cannot be parsed as a date. + */ + func toDate(format : String? = "yyyy-MM-dd") -> NSDate? { + let text = self.trimmed().lowercaseString + var dateFmt = NSDateFormatter() + dateFmt.timeZone = NSTimeZone.defaultTimeZone() + if let fmt = format { + dateFmt.dateFormat = fmt + } + return dateFmt.dateFromString(text) + } + + /** + Parses a string containing a date and time into an optional NSDate if the string is a well formed. + The default format is yyyy-MM-dd hh-mm-ss, but can be overriden. + + :returns: A NSDate parsed from the string or nil if it cannot be parsed as a date. + */ + func toDateTime(format : String? = "yyyy-MM-dd hh-mm-ss") -> NSDate? { + return toDate(format: format) + } + +} + +/** + Repeats the string first n times +*/ +public func * (first: String, n: Int) -> String { + + var result = String() + + n.times { + result += first + } + + return result + +} + +// Pattern matching using a regular expression +public func =~ (string: String, pattern: String) -> Bool { + + let regex = ExSwift.regex(pattern, ignoreCase: false)! + let matches = regex.numberOfMatchesInString(string, options: nil, range: NSMakeRange(0, string.length)) + + return matches > 0 + +} + +// Pattern matching using a regular expression +public func =~ (string: String, regex: NSRegularExpression) -> Bool { + + let matches = regex.numberOfMatchesInString(string, options: nil, range: NSMakeRange(0, string.length)) + + return matches > 0 + +} + +// This version also allowes to specify case sentitivity +public func =~ (string: String, options: (pattern: String, ignoreCase: Bool)) -> Bool { + + if let matches = ExSwift.regex(options.pattern, ignoreCase: options.ignoreCase)?.numberOfMatchesInString(string, options: nil, range: NSMakeRange(0, string.length)) { + return matches > 0 + } + + return false + +} + +// Match against all the alements in an array of String +public func =~ (strings: [String], pattern: String) -> Bool { + + let regex = ExSwift.regex(pattern, ignoreCase: false)! + + return strings.all { $0 =~ regex } + +} + +public func =~ (strings: [String], options: (pattern: String, ignoreCase: Bool)) -> Bool { + + return strings.all { $0 =~ options } + +} + +// Match against any element in an array of String +public func |~ (strings: [String], pattern: String) -> Bool { + + let regex = ExSwift.regex(pattern, ignoreCase: false)! + + return strings.any { $0 =~ regex } + +} + +public func |~ (strings: [String], options: (pattern: String, ignoreCase: Bool)) -> Bool { + + return strings.any { $0 =~ options } + +} diff --git a/kplayer/master/MasterViewController.swift b/kplayer/master/MasterViewController.swift index 2ca8904..60b7490 100644 --- a/kplayer/master/MasterViewController.swift +++ b/kplayer/master/MasterViewController.swift @@ -51,17 +51,11 @@ class MasterViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - loadDirs("/srv/samba/ren/fav") - loadDirs("/srv/samba/ren/knk_archiv") - loadDirs("/srv/samba/ren/knk_archiv2") - loadDirs("/srv/samba/ren/knk_archiv3") - loadDirs("/srv/samba/ren/knk_archiv4") - // Do any additional setup after loading the view, typically from a nib. - self.navigationItem.leftBarButtonItem = self.editButtonItem() +// self.navigationItem.leftBarButtonItem = self.editButtonItem() - let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:") - self.navigationItem.rightBarButtonItem = addButton +// let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:") +// self.navigationItem.rightBarButtonItem = addButton if let split = self.splitViewController { let controllers = split.viewControllers self.detailViewController = controllers[controllers.count-1].topViewController as? DetailViewController @@ -75,9 +69,6 @@ class MasterViewController: UITableViewController { // Dispose of any resources that can be recreated. } - func insertNewObject(sender: AnyObject) { - } - override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) { let pc = MediaPhotoController() @@ -98,7 +89,86 @@ class MasterViewController: UITableViewController { presentViewController(navController, animated: false, completion: nil) } - // MARK: - Segues + override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { +println ("\(indexPath.row)") + let selectedItem = items[indexPath.row] + + if (selectedItem.type == ItemType.FOLDER) { + if (!selectedItem.children.isEmpty) { + if selectedItem.children[0].type != ItemType.FOLDER { + performSegueWithIdentifier("showDetail", sender: self) + return + } + } + } + + let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) +// Imagine it's called MyCustomTableVC + let vc = mainStoryboard.instantiateViewControllerWithIdentifier("mastertable") as! MasterViewController + + if (selectedItem.type == ItemType.FOLDER) { + vc.navigationItem.title = selectedItem.name; + + if !selectedItem.loaded { + let dir = selectedItem.root + "/" + selectedItem.path +// NetworkManager.sharedInstance.loadDirs(dir) { (g) in + NetworkManager.sharedInstance.loadPicDirs(dir) { (g) in + for f in g { + vc.addItem(f) + } + selectedItem.children = vc.items + selectedItem.loaded = true + + var all :[MediaItem]? + + for f in selectedItem.children { + if f.type == ItemType.FOLDER && f.path != "" { + self.navigationController!.pushViewController(vc, animated: true) + return + } + if f.type == ItemType.FOLDER && f.path == "" { + all = f.children + break + } + } + + if all != nil { + selectedItem.children = all! + selectedItem.loaded = false + + for f in all! { + f.loaded = false + f.parent = selectedItem + } + } + + self.performSegueWithIdentifier("showDetail", sender: self) + return + } + } + else { + vc.items = selectedItem.children + navigationController!.pushViewController(vc, animated:true) + } + } + + if (selectedItem.type == ItemType.ROOT) { + NetworkManager.sharedInstance.listDirs(selectedItem.root, completionHandler: { + (i) in + vc.items = i + vc.tableView.reloadData() + }) + vc.navigationItem.title = selectedItem.name; + navigationController!.pushViewController(vc, animated:true) + } + + + } + + override func shouldPerformSegueWithIdentifier(identifier:String!, sender: AnyObject?) -> Bool { + return false; + } + // MARK: - Segues override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "showDetail" { @@ -106,6 +176,8 @@ class MasterViewController: UITableViewController { let item = items[indexPath.row] let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController controller.detailItem = item + controller.defaultItemSize = CGSize(width: 300, height: 300) + item.children.sort({ $0.sortName < $1.sortName }) @@ -115,6 +187,8 @@ class MasterViewController: UITableViewController { i.index = j++ } +// controller.collectionView.reloadData() + NetworkManager.sharedInstance.loadItems(item) controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() controller.navigationItem.leftItemsSupplementBackButton = true @@ -136,7 +210,6 @@ class MasterViewController: UITableViewController { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell self.configureCell(cell, atIndexPath: indexPath) - cell.accessoryType = UITableViewCellAccessoryType.DetailDisclosureButton return cell } @@ -154,6 +227,13 @@ class MasterViewController: UITableViewController { func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) { let object = items[indexPath.row] cell.textLabel!.text = object.path + +// if !object.children.isEmpty && object.children[0].type == ItemType.VIDEO { +// cell.accessoryType = UITableViewCellAccessoryType.DetailDisclosureButton +// } +// else { + cell.accessoryType = UITableViewCellAccessoryType.None +// } } diff --git a/kplayer/photo/MediaPhotoController.swift b/kplayer/photo/MediaPhotoController.swift index 1aad8c1..a7048a4 100644 --- a/kplayer/photo/MediaPhotoController.swift +++ b/kplayer/photo/MediaPhotoController.swift @@ -14,22 +14,16 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView var requests = Array() - // var fetchers = [NetworkFetcher]() - - let hqCache = Shared.imageCache // Cache(name: "hq") - lazy var operationQueue: NSOperationQueue = { var queue = NSOperationQueue() queue.name = "Photo queue" - queue.maxConcurrentOperationCount = 1 + queue.maxConcurrentOperationCount = 5 return queue }() override func viewDidLoad() { super.viewDidLoad() - hqCache.addFormat(Format(name: "icons", diskCapacity: 0)) - let backButton = UIBarButtonItem(barButtonSystemItem: .Cancel, target: self, action: Selector("back")) navigationItem.leftBarButtonItems = [backButton] @@ -40,6 +34,21 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView self.photoAlbumView.reloadData(); self.photoScrubberView.reloadData(); + + let back = NSURLSessionConfiguration.ephemeralSessionConfiguration() + + let bsession = NSURLSession(configuration: back) + + for i in items { + let URL = NSURL(string: i.thumbUrlAbsolute)! + let fetcher = MediaFetcher(URL: URL, session: bsession) + let fetch = Shared.imageCache.fetch(fetcher: fetcher, formatName: HanekeGlobals.Cache.OriginalFormatName, failure: nil, success: nil) + + fetch.onSuccess { + s in + println ("preload \(s)") + } + } } func back() { @@ -86,156 +95,76 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView self.title = "loading" // self.photoAlbumView.loadingImage = UIImage(named: "Kirschkeks-256x256.png") -println("\(count(items))") + println("\(count(items))") } func photoAlbumScrollView(photoAlbumScrollView: NIPhotoAlbumScrollView!, photoAtIndex: Int, photoSize: UnsafeMutablePointer, isLoading: UnsafeMutablePointer, originalPhotoDimensions: UnsafeMutablePointer) -> UIImage! { let c = count(items) - println ("Index: \(photoAtIndex) of \(c)") + // println("Index: \(photoAtIndex) of \(c)") let newItem = items[photoAtIndex] let hqURL = NSURL(string: newItem.imageUrlAbsolute)! + let URL = NSURL(string: newItem.thumbUrlAbsolute)! + var image: UIImage? = nil var size = NIPhotoScrollViewPhotoSizeUnknown - hqCache.fetch(key: hqURL.absoluteString!, formatName: HanekeGlobals.Cache.OriginalFormatName).onSuccess { - i in - println ("cached \(newItem.imageUrlAbsolute)") - image = i - size = NIPhotoScrollViewPhotoSizeOriginal - self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) - } -// var fetcher = Shared.imageCache.fetch(URL: URL) -// let fs = Shared.imageCache.formats -// if let (format, memoryCache, diskCache) = fs[HanekeGlobals.Cache.OriginalFormatName] { -// if memoryCache.objectForKey(URL!.absoluteString!) != nil { -// Shared.imageCache.fetch(URL: URL).onSuccess { -// i in -// println ("loaded \(newItem.imageUrlAbsolute)") -// image = i -// size = NIPhotoScrollViewPhotoSizeOriginal -// self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) -// } -// } -// } - -// if (fetcher != nil) { -// fetcher!.cancelFetch() -// } -// fetcher = NetworkFetcher(URL: URL) -// let fetch = Shared.imageCache.fetch(fetcher: fetcher!, formatName: HanekeGlobals.Cache.OriginalFormatName, failure: nil, success: nil) -// -// fetch.onSuccess { -// i in -// println ("loaded \(newItem.imageUrlAbsolute)") -// image = i -// size = NIPhotoScrollViewPhotoSizeOriginal -// self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) -// } - - if let i = image { - isLoading[0] = false - } - else { - println ("not cached \(newItem.imageUrlAbsolute)") - - let URL = NSURL(string: newItem.thumbUrlAbsolute)! - println (newItem.thumbUrlAbsolute) - var image: UIImage? = nil - - for r in requests { - let pages = self.photoAlbumView.visiblePages() as NSMutableSet! - var ok = false - for page in pages.allObjects { - if (page.pageIndex == r.index) { - ok = true - break - } - } - - if !ok { - println("canceled \(r.imageURL)") - r.cancel() - requests.remove(r) - } - } - -// let op = HanekeFetchOperation(baseUrl: URL, succeeder: { -// i in -// image = i -// size = NIPhotoScrollViewPhotoSizeThumbnail -// self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) -// println ("thumb \(URL.absoluteString!)") -// photoSize[0] = size -// }, index: photoAtIndex) -// requests.append(op) -// self.operationQueue.addOperation(op) - - let fetcher = NetworkFetcher(URL: URL) - let fetch = Shared.imageCache.fetch(fetcher: fetcher, formatName: HanekeGlobals.Cache.OriginalFormatName, failure: nil, success: nil) - - fetch.onSuccess { + Shared.imageCache.fetch(key: URL.absoluteString!, formatName: HanekeGlobals.Cache.OriginalFormatName).onSuccess { i in - println ("loaded \(newItem.imageUrlAbsolute)") image = i size = NIPhotoScrollViewPhotoSizeThumbnail - self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) + self.photoAlbumView.didLoadPhoto(image, atIndex: photoAtIndex, photoSize: size) } + if let i = image { + isLoading[0] = false + } else { + let fetcher = NetworkFetcher(URL: URL) + let fetch = Shared.imageCache.fetch(fetcher: fetcher, formatName: HanekeGlobals.Cache.OriginalFormatName, failure: nil, success: nil) - let op2 = ImageLoadOperation(imageURL: hqURL, succeeder: { + fetch.onSuccess { i in - println ("loaded \(newItem.imageUrlAbsolute) at \(photoAtIndex)") + println("thumb loaded \(newItem.imageUrlAbsolute)") image = i - size = NIPhotoScrollViewPhotoSizeOriginal - self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) - photoSize[0] = size - }, index: photoAtIndex) - requests.append(op2) - op2.qualityOfService = NSQualityOfService.UserInteractive - self.operationQueue.addOperation(op2) + size = NIPhotoScrollViewPhotoSizeThumbnail + self.photoAlbumView.didLoadPhoto(image, atIndex: photoAtIndex, photoSize: size) + } + } + for r in requests { + let pages = self.photoAlbumView.visiblePages() as NSMutableSet! + var ok = false + for page in pages.allObjects { + if (page.pageIndex == r.index) { + ok = true + break + } + } - // NSURLSession.sharedSession().invalidateAndCancel(); + if !ok { + r.cancel() + requests.remove(r) + } + } -// for f in fetchers { - // println ("cancel \(f.key)") - // f.cancelFetch() - // } - // fetchers.removeAll() -// let fetcher = NetworkFetcher(URL: hqURL) -// fetchers.append(fetcher) -// -// let fetch = hqCache.fetch(fetcher: fetcher, formatName: "icons", failure: nil, success: nil) -// -// fetch.onSuccess { -// i in -// println ("loaded \(newItem.imageUrlAbsolute) at \(photoAtIndex)") -// image = i -// size = NIPhotoScrollViewPhotoSizeOriginal -// self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) -// photoSize[0] = size -// return -// } - -// Shared.imageCache.fetch(URL: hqURL).onSuccess { -// i in -// println ("loaded \(newItem.imageUrlAbsolute)") -// image = i -// size = NIPhotoScrollViewPhotoSizeOriginal -// self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) -// } - - if (image == nil) { - isLoading[0] = true - } + let op2 = ImageLoadOperation(imageURL: hqURL, succeeder: { + i in + println("image loaded \(newItem.imageUrlAbsolute) at \(photoAtIndex)") + size = NIPhotoScrollViewPhotoSizeOriginal + self.photoAlbumView.didLoadPhoto(i, atIndex: photoAtIndex, photoSize: size) + photoSize[0] = size + }, index: photoAtIndex) + requests.append(op2) + operationQueue.addOperation(op2) + + if (image == nil) { + isLoading[0] = true } + photoSize[0] = size -// if image == nil { return UIImage(named: "Kirschkeks-256x256.png") } -// self.photoAlbumView.didLoadPhoto(image, atIndex:photoAtIndex, photoSize:size) return image } @@ -275,7 +204,6 @@ println("\(count(items))") } let c = count(items) - println ("Thumb Index: \(thumbnailIndex) of \(c)") let newItem = items[thumbnailIndex] let URL = NSURL(string: newItem.thumbUrlAbsolute)! @@ -284,14 +212,21 @@ println("\(count(items))") Shared.imageCache.fetch(URL: URL).onSuccess { i in image = i - println ("thumb scrubber \(URL.absoluteString!)") self.photoScrubberView.didLoadThumbnail(i, atIndex: thumbnailIndex); } - // if image == nil { return UIImage(named: "Kirschkeks-256x256.png") } return image } +} +class MediaFetcher : NetworkFetcher { + let bsession: NSURLSession? + + init(URL : NSURL, session: NSURLSession) { + self.bsession = session + super.init(URL: URL) + } + override public var session : NSURLSession { return bsession! } } diff --git a/kplayer/util/HanekeFetchOperation.swift b/kplayer/util/HanekeFetchOperation.swift new file mode 100644 index 0000000..e3cbbff --- /dev/null +++ b/kplayer/util/HanekeFetchOperation.swift @@ -0,0 +1,32 @@ +// +// Created by Marco Schmickler on 21.03.15. +// Copyright (c) 2015 Marco Schmickler. All rights reserved. +// + +import Foundation +import Haneke + +class HanekeFetchOperation: NSOperation { + let baseUrl: NSURL + let succeeder: Fetch.Succeeder + let index: Int + + init(baseUrl: NSURL, succeeder: Fetch.Succeeder, index: Int) { + self.baseUrl = baseUrl + self.succeeder = succeeder + self.index = index + } + + override func main() { + if self.cancelled { + return + } + + Shared.imageCache.fetch(URL: baseUrl).onSuccess { i in + + dispatch_async(dispatch_get_main_queue(), { self.succeeder(i) }) + + } + } + +} diff --git a/kplayer/util/ImageLoadOperation.swift b/kplayer/util/ImageLoadOperation.swift new file mode 100644 index 0000000..335c780 --- /dev/null +++ b/kplayer/util/ImageLoadOperation.swift @@ -0,0 +1,46 @@ +// +// Created by Marco Schmickler on 21.03.15. +// Copyright (c) 2015 Marco Schmickler. All rights reserved. +// + +import Foundation +import Alamofire +import Darwin + +class ImageLoadOperation: NSOperation { + public typealias Succeeder = (UIImage) -> () + + let imageURL: NSURL + let succeeder: Succeeder + let index: Int + + + init(imageURL: NSURL, succeeder: Succeeder, index: Int) { + self.imageURL = imageURL + self.succeeder = succeeder + self.index = index + } + + override func main() { + if self.cancelled { + return + } + + usleep(100000) + + if self.cancelled { + return + } + + Alamofire.request(.GET, imageURL).validate().responseImage() { + (request, _, image, error) in + + if error == nil { +// if request.URLString.isEqual(self.imageURL) { + self.succeeder(image!) + // } + } + } + } + +} diff --git a/kplayer/util/alamoimage.swift b/kplayer/util/alamoimage.swift new file mode 100644 index 0000000..c018aaf --- /dev/null +++ b/kplayer/util/alamoimage.swift @@ -0,0 +1,28 @@ +// +// Created by Marco Schmickler on 22.06.15. +// Copyright (c) 2015 Marco Schmickler. All rights reserved. +// + +import Foundation +import Alamofire + +extension Alamofire.Request { + class func imageResponseSerializer() -> Serializer { + return { request, response, data in + if data == nil { + return (nil, nil) + } + + let i = UIImage(data: data!) + // let image = i.hnk_decompressedImage() + + return (i, nil) + } + } + + func responseImage(completionHandler: (NSURLRequest, NSHTTPURLResponse?, UIImage?, NSError?) -> Void) -> Self { + return response(serializer: Request.imageResponseSerializer(), completionHandler: { (request, response, image, error) in + completionHandler(request, response, image as? UIImage, error) + }) + } +} \ No newline at end of file