From 387afeadd9a2cba0a253455e8198bb908b4aaae7 Mon Sep 17 00:00:00 2001 From: Marco Schmickler Date: Sun, 23 Dec 2018 17:46:15 +0100 Subject: [PATCH] Swift 4.2 --- kplayer.xcodeproj/project.pbxproj | 4 + kplayer/core/NetworkManager.swift | 60 ++++- kplayer/core/ThumbnailCache.swift | 187 +++++++++++++ kplayer/detail/DetailViewController.swift | 57 +--- kplayer/photo/MediaPhotoController.swift | 310 ++++++++-------------- kplayer/util/HanekeFetchOperation.swift | 4 +- kplayer/util/ImageLoadOperation.swift | 58 ++-- kplayer/util/UIImageExtension.swift | 14 + kplayerTests/kplayerTests.swift | 21 +- 9 files changed, 416 insertions(+), 299 deletions(-) create mode 100644 kplayer/core/ThumbnailCache.swift diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index 70c307b..635bc72 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 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 */; }; + 1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */; }; 1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; }; 1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; }; 1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */; }; @@ -76,6 +77,7 @@ 1C736D981F8315FFD7D40695 /* KBMPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KBMPlayer.swift; sourceTree = ""; }; 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = ""; }; 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; + 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailCache.swift; sourceTree = ""; }; 1C736F9338CE36708244D42A /* DataLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataLoadOperation.swift; sourceTree = ""; }; 6D522F61736592330F481B4F /* Pods-kplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-kplayer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-kplayer/Pods-kplayer.debug.xcconfig"; sourceTree = ""; }; 8B75159FFCD5A882E6F167FE /* Pods_kplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_kplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -164,6 +166,7 @@ 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */, 1C73688DAB88F9360FB62A49 /* MediaItem.swift */, 1C736253AB7A95EA41B605B7 /* ItemModel.swift */, + 1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */, ); path = core; sourceTree = ""; @@ -447,6 +450,7 @@ 1C7369763AB6C73472E11B55 /* KBMPlayer.swift in Sources */, 1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */, 1C7362A6FA1C5DA0B0677F1E /* readme.md in Sources */, + 1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift index f1068b0..4a1b223 100644 --- a/kplayer/core/NetworkManager.swift +++ b/kplayer/core/NetworkManager.swift @@ -46,17 +46,11 @@ class NetworkManager { print("Empfange \(json.count) Ergebnisse") for s in json { -// print(s) if s.hasSuffix(".mp4") || s.hasSuffix(".m4v") { let l = s.count let name = NSURL(fileURLWithPath: s).lastPathComponent! var pathlen = l - len - name.count -// if (pathlen > 1000) { -// print(pathlen) -// print(name) -// print(s) -// } if (pathlen < 2) { pathlen = 2 } @@ -281,6 +275,10 @@ class NetworkManager { } } + func deleteThumb(_ path: String) { + Alamofire.request(NetworkManager.sharedInstance.baseurl + "/service/deletethumb\(path)") + } + func favItem(_ item: MediaItem) { let url = baseurl + "/service/linkfav" + item.fullPath @@ -340,4 +338,54 @@ class NetworkManager { } + func loadDetails(items: MediaItem, result: @escaping ([MediaItem]) -> () ) { + let len = items.root.count + let url = NetworkManager.sharedInstance.baseurl + "/service/listfiles" + items.fullPath + + print(items) + print(url) + + Alamofire.request(url).responseJSON { + (response) in + + if let json = response.result.value { + var im = [MediaItem]() + + for s in json as! [String] { + + if s.lowercased().hasSuffix(".jpg") { + let l = s.count + let name = NSURL(fileURLWithPath: s).lastPathComponent! + var pathlen = l - len - name.count + +// if (pathlen > 1000) { + print(pathlen) + print(name) + print(s) +// } + if (pathlen < 2) { + pathlen = 2 + } + + let path = (s as NSString).substring(with: NSMakeRange(len + 1, pathlen - 2)) + + let folderName = NSURL(fileURLWithPath: path).lastPathComponent! + let fl = path.count + let pfl = fl - folderName.count + + print("\(folderName) \(pfl)") + let fpath = s.substringLeft(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) + } + } + } + result(im) + } + } + } + } diff --git a/kplayer/core/ThumbnailCache.swift b/kplayer/core/ThumbnailCache.swift new file mode 100644 index 0000000..c50a077 --- /dev/null +++ b/kplayer/core/ThumbnailCache.swift @@ -0,0 +1,187 @@ +// +// Created by Marco Schmickler on 2018-12-23. +// Copyright (c) 2018 Marco Schmickler. All rights reserved. +// + +import Foundation +import UIKit + +class ThumbnailCache { + let items: [MediaItem] + var urlSession: Foundation.URLSession? + + var sessionOwner = false + + let dateFormatter = DateFormatter() + + var imageCache = NSCache() + var total = 0 + var currentIndex = 80 + + let refresh: () -> Void; + + var loading = true; + + init(items: [MediaItem], refresh: @escaping () -> Void) { + self.items = items + self.refresh = refresh + + imageCache.totalCostLimit = 1024 * 1024 * 1024 + + if urlSession == nil { + sessionOwner = true + let configuration = URLSessionConfiguration.ephemeral; // backgroundSessionConfigurationWithIdentifier("imageLoad"); + configuration.httpMaximumConnectionsPerHost = 10; + configuration.timeoutIntervalForRequest = 100; + configuration.timeoutIntervalForResource = 200; + urlSession = Foundation.URLSession(configuration: configuration, delegate: nil, delegateQueue: nil); + } + + preload(0, count: 20) + preload(20, count: 20) + preload(40, count: 20) + preload(60, count: 20) + + usleep(10000) + } + + func loadData(_ d: Data, start: Int, end: Int) { + let bytes = Array(UnsafeBufferPointer(start: (d as NSData).bytes.bindMemory(to: UInt8.self, capacity: d.count), count: d.count)) + let string1 = NSString(data: d, encoding: String.Encoding.utf8.rawValue) + print(string1) + var index = 0 + let fHi = (Int(bytes[index]) << 24) + (Int(bytes[index+1]) << 16) + let f = fHi + (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) + index += 4 + let time = dateFormatter.string(from: Date()) + print("\(time) start \(start) count \(f) size \(d.count)") + + var rest = self.items.count - self.currentIndex + + if rest > 20 { + rest = 20 + } + + if rest > 0 { + self.preload(self.currentIndex, count: rest) + self.currentIndex += rest + } else { + loading = false + DispatchQueue.main.async(execute: refresh) + } + + for bild in start ..< end { + let sizeHi = (Int(bytes[index]) << 24) + (Int(bytes[index+1]) << 16) + let size = (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) + sizeHi + index += 4 + + if size > 0 { + // var buf = //UnsafeMutablePointer(&bytes[index]) + let part = d.subdata(in: index ..< (index + size)) + if let img = UIImage(data: part) { + let imageRef = img.cgImage; + self.total += d.count + + let i = self.items[bild] +// print("\(time) preload combi image loaded \(i.name) cost \(cost) total\(self.total)") + self.imageCache.setObject(img, forKey: i.thumbUrlAbsolute as AnyObject, cost: size) + } + } + index += size + } + } + + func preload(_ start: Int, count: Int) { + + dateFormatter.dateFormat = "HH:mm:ss.SSSZ" + + var preview = "" + var end = start + count + if end > items.count { + end = items.count + } + + if end < start { + return + } + + for k in start ..< end { + let tu = items[k].thumbUrl!.replacingOccurrences(of: "?preview=true", with: "") + preview += "\(tu);" + } + + preview = preview.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! + + let u2 = "\(NetworkManager.sharedInstance.baseurl)/service/preload?preview=\(preview)" + let u3 = URL(string: u2) + + if let URL = u3 { + let time = dateFormatter.string(from: Date()) + print("\(time) preload image \(u2)") + + urlSession!.dataTask(with: URL, completionHandler: { + (da, response, error) in + if let d = da { + DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.high).async { + () -> Void in + self.loadData(d, start: start, end: end) + } + } + }) .resume() + } else { + print("## Invalid URL: \(u2)") + } + } + + func cancel() { + imageCache.removeAllObjects() + + if sessionOwner { + urlSession!.invalidateAndCancel() + } + } + + func getThumbnail(_ item: MediaItem) -> UIImage? { + let u1 = URL(string: item.thumbUrlAbsolute) + + if let URL = u1 { + return imageCache.object(forKey: URL.absoluteString as AnyObject) as? UIImage + } + + return nil + } + + func loadThumbnail(thumbnailIndex: Int, thumbnail: @escaping (UIImage, Int) -> ()) -> UIImage? { + var image: UIImage? + let newItem = items[thumbnailIndex] + let u1 = URL(string: newItem.thumbUrlAbsolute) + + if let URL = u1 { + + image = imageCache.object(forKey: URL.absoluteString as AnyObject) as? UIImage + + if (image == nil && (!loading||thumbnailIndex == 0)) { + let task: URLSessionDataTask = urlSession!.dataTask(with: URL) { + (da, response, error) in + + if let d = da { + if let i = UIImage(data: d) { +// println("thumb image loaded \(newItem.thumbUrlAbsolute)") + let imageRef = i.cgImage; + self.imageCache.setObject(i, forKey: newItem.thumbUrlAbsolute as AnyObject, cost: d.count) + + DispatchQueue.main.async { + thumbnail(i, thumbnailIndex); + } + } + } + } + task.resume() + } + } + else { + print("## Invalid URL: \(newItem.thumbUrlAbsolute)") + } + return image + } +} diff --git a/kplayer/detail/DetailViewController.swift b/kplayer/detail/DetailViewController.swift index 1f3e51b..b62bf78 100644 --- a/kplayer/detail/DetailViewController.swift +++ b/kplayer/detail/DetailViewController.swift @@ -185,8 +185,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout let ms = Int(t * 1000) let p = c.snapshotDirPathForVideo + "\(ms).jpg" let pt = c.snapshotDirPathForVideo + "\(ms)_thumb.jpg" - Alamofire.request(NetworkManager.sharedInstance.baseurl + "/service/deletethumb\(p)") - Alamofire.request(NetworkManager.sharedInstance.baseurl + "/service/deletethumb\(pt)") + + NetworkManager.sharedInstance.deleteThumb(p) + NetworkManager.sharedInstance.deleteThumb(pt) } self.collectionView.reloadData() } @@ -254,7 +255,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout headerView.setItem(items) return headerView default: - assert(false, "Unexpected element kind") + fatalError("Unexpected element kind") } } @@ -276,53 +277,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout showVideo() } else { - let len = items.root.count - let url = NetworkManager.sharedInstance.baseurl + "/service/listfiles" + items.fullPath - - print(items) - print(url) - - Alamofire.request(url).responseJSON { - (response) in - - if let json = response.result.value { - var im = [MediaItem]() - - for s in json as! [String] { - - if s.lowercased().hasSuffix(".jpg") { - let l = s.count - let name = NSURL(fileURLWithPath: s).lastPathComponent! - var pathlen = l - len - name.count - -// if (pathlen > 1000) { - print(pathlen) - print(name) - print(s) -// } - if (pathlen < 2) { - pathlen = 2 - } - - let path = (s as NSString).substring(with: NSMakeRange(len + 1, pathlen - 2)) - - let folderName = NSURL(fileURLWithPath: path).lastPathComponent! - let fl = path.count - let pfl = fl - folderName.count - - print("\(folderName) \(pfl)") - let fpath = s.substringLeft(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) - } - } - } - self.showPhotos(im) - } - } + NetworkManager.sharedInstance.loadDetails(items: items, result: { (im: [MediaItem]) in + self.showPhotos(im) + }) } } } diff --git a/kplayer/photo/MediaPhotoController.swift b/kplayer/photo/MediaPhotoController.swift index 52055e8..49c7595 100644 --- a/kplayer/photo/MediaPhotoController.swift +++ b/kplayer/photo/MediaPhotoController.swift @@ -7,32 +7,37 @@ import Foundation import Nimbus import Alamofire -class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollViewDataSource, NIPhotoScrubberViewDataSource, URLSessionDelegate { +/** +NIPhotoScrollView + + if (NIPhotoScrollViewPhotoSizeThumbnail != photoSize) { + // Don't let minScale exceed maxScale. (If the image is smaller than the screen, we + // don't want to force it to be zoomed.) + // todo marco minScale = MIN(minScale, maxScale); + } + +*/ + +class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollViewDataSource, NIPhotoScrubberViewDataSource { var items = [MediaItem]() var completionHandler: (() -> Void)? - var requests = Array() - - var imageCache = NSCache() - var total = 0 + var loadOriginal : ImageLoadOperation? + var loadNext : ImageLoadOperation? + var nextImage : UIImage? + var nextImageIndex = 0 var slide = 0 var timer: Timer? - var currentIndex = 80 - - var urlSession: Foundation.URLSession? - - var sessionOwner = false - - let dateFormatter = DateFormatter() + var thumbnails: ThumbnailCache? lazy var operationQueue: OperationQueue = { var queue = OperationQueue() queue.name = "Photo queue" - queue.maxConcurrentOperationCount = 3 + queue.maxConcurrentOperationCount = 1 return queue }() @@ -46,24 +51,9 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView title = "(\(idx + 1) / \(photoAlbumView.numberOfPages)) \(selected.path) \(selected.name)" } - open func URLSession(_ session: Foundation.URLSession, didBecomeInvalidWithError error: NSError?) { - print(error) - } - override func viewDidLoad() { super.viewDidLoad() - if urlSession == nil { - sessionOwner = true - let configuration = URLSessionConfiguration.ephemeral; // backgroundSessionConfigurationWithIdentifier("imageLoad"); - configuration.httpMaximumConnectionsPerHost = 10; - configuration.timeoutIntervalForRequest = 100; - configuration.timeoutIntervalForResource = 200; - urlSession = Foundation.URLSession(configuration: configuration, delegate: self, delegateQueue: nil); - } - - imageCache.totalCostLimit = 1024 * 1024 * 1024 - let backButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(MediaPhotoController.back)) let slideButton = UIBarButtonItem(barButtonSystemItem: .fastForward, target: self, action: #selector(MediaPhotoController.slideShow)) navigationItem.leftBarButtonItems = [backButton, slideButton] @@ -74,110 +64,19 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView setChromeVisibility(true, animated: true) - self.photoAlbumView.reloadData(); - - preload(0, count: 20) - preload(20, count: 20) - preload(40, count: 20) - preload(60, count: 20) - } - - func loadData(_ d: Data, start: Int, end: Int) { - let bytes = Array(UnsafeBufferPointer(start: (d as NSData).bytes.bindMemory(to: UInt8.self, capacity: d.count), count: d.count)) - let string1 = NSString(data: d, encoding: String.Encoding.utf8.rawValue) - print(string1) - var index = 0 - let fHi = (Int(bytes[index]) << 24) + (Int(bytes[index+1]) << 16) - let f = fHi + (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) -// let f = (bytes[index++] << 24)+(bytes[index++] << 16)+(bytes[index++] << 8) + bytes[index++] - index += 4 - let time = dateFormatter.string(from: Date()) - print("\(time) start \(start) count \(f) size \(d.count)") - - var rest = self.items.count - self.currentIndex - - if rest > 20 { - rest = 20 - } - - if rest > 0 { - self.preload(self.currentIndex, count: rest) - self.currentIndex += rest - } else { - DispatchQueue.main.async(execute: { - () -> Void in - self.photoScrubberView.reloadData(); - }) - } - - for bild in start ..< end { - let sizeHi = (Int(bytes[index]) << 24) + (Int(bytes[index+1]) << 16) - let size = (Int(bytes[index+2]) << 8) + Int(bytes[index+3]) + sizeHi - index += 4 - - if size > 0 { - // var buf = //UnsafeMutablePointer(&bytes[index]) - let part = d.subdata(in: index ..< (index + size)) - if let img = UIImage(data: part) { - let imageRef = img.cgImage; - self.total += d.count - - let i = self.items[bild] -// print("\(time) preload combi image loaded \(i.name) cost \(cost) total\(self.total)") - self.imageCache.setObject(img, forKey: i.thumbUrlAbsolute as AnyObject, cost: size) - } - } - index += size - } - } - - func preload(_ start: Int, count: Int) { - - dateFormatter.dateFormat = "HH:mm:ss.SSSZ" - - var preview = "" - var end = start + count - if end > items.count { - end = items.count - } - - if end < start { - return - } - - for k in start ..< end { - let tu = items[k].thumbUrl!.replacingOccurrences(of: "?preview=true", with: "") - preview += "\(tu);" - } - - preview = preview.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! + thumbnails = ThumbnailCache(items: items, refresh: { + () -> Void in + self.photoScrubberView.reloadData(); + }); - let u2 = "\(NetworkManager.sharedInstance.baseurl)/service/preload?preview=\(preview)" - let u3 = URL(string: u2) - - if let URL = u3 { - let time = dateFormatter.string(from: Date()) - print("\(time) preload image \(u2)") + self.photoAlbumView.reloadData(); - urlSession!.dataTask(with: URL, completionHandler: { - (da, response, error) in - if let d = da { - DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.high).async { - () -> Void in - self.loadData(d, start: start, end: end) - } - } - }) .resume() -// op1.qualityOfService = NSQualityOfService.Background -// backgroundOperationQueue.addOperation(op1) - } else { - print("## Invalid URL: \(u2)") - } + pagingScrollViewDidChangePages(photoAlbumView) } override func didReceiveMemoryWarning() { print("warning") - imageCache.removeAllObjects() + thumbnails?.cancel() super.didReceiveMemoryWarning() } @@ -196,12 +95,9 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView } @objc func back() { - imageCache.removeAllObjects() operationQueue.cancelAllOperations() - if sessionOwner { - urlSession!.invalidateAndCancel() - } + thumbnails?.cancel(); completionHandler!() } @@ -286,8 +182,6 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView } let pc = MediaPhotoController() - pc.urlSession = self.urlSession! - pc.items = im pc.completionHandler = { self.dismiss(animated: true, completion: nil); @@ -351,58 +245,19 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView var size = NIPhotoScrollViewPhotoSizeUnknown - for r in requests { - let pages = self.photoAlbumView.visiblePages() as NSMutableSet! - var ok = false - for page in (pages?.allObjects)! { - if ((page as AnyObject).pageIndex == r.index) { - ok = true - break - } - } - - if !ok { - r.cancel() - requests.remove(at: requests.index(of: r)!) - } - } - - image = imageCache.object(forKey: u5.absoluteString as AnyObject) as? UIImage + image = thumbnails?.getThumbnail(newItem) if image != nil { size = NIPhotoScrollViewPhotoSizeThumbnail isLoading[0] = false } else { - let ur = URL(string: newItem.thumbUrlAbsolute)! - - let op3 = DataLoadOperation(imageURL: ur, succeeder: { - d in - if d.count > 0 { - if let i = UIImage(data: d) { -// println("thumb preload image loaded \(newItem.imageUrlAbsolute)") - let imageRef = i.cgImage; - self.imageCache.setObject(d as AnyObject, forKey: newItem.thumbUrlAbsolute as AnyObject, cost: d.count) - size = NIPhotoScrollViewPhotoSizeThumbnail - self.photoAlbumView.didLoadPhoto(i, at: photoAtIndex, photoSize: size) - self.photoScrubberView.didLoadThumbnail(i, at: photoAtIndex); - } - } - }, index: photoAtIndex, session: urlSession!) - op3.qualityOfService = QualityOfService.userInteractive - operationQueue.addOperation(op3) - } - + let thumbnail: (UIImage, Int) -> () = { (i: UIImage, thumbnailIndex: Int) in + self.photoAlbumView.didLoadPhoto(i, at: photoAtIndex, photoSize: NIPhotoScrollViewPhotoSizeThumbnail) + self.photoScrubberView.didLoadThumbnail(i, at: thumbnailIndex) + } - let op2 = ImageLoadOperation(imageURL: hqURL, succeeder: { - i in -// println("image loaded \(newItem.imageUrlAbsolute) at \(photoAtIndex)") - size = NIPhotoScrollViewPhotoSizeOriginal - self.photoAlbumView.didLoadPhoto(i, at: photoAtIndex, photoSize: size) - photoSize[0] = size - }, index: photoAtIndex) - op2.qualityOfService = QualityOfService.userInitiated - requests.append(op2) - operationQueue.addOperation(op2) + let image = thumbnails?.loadThumbnail(thumbnailIndex: photoAtIndex, thumbnail: thumbnail) + } if (image == nil) { isLoading[0] = true @@ -418,6 +273,70 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView } } + public override func pagingScrollViewDidChangePages(_ pagingScrollView: NIPagingScrollView!) { + setChromeTitle() + + let currentIndex: Int = self.photoAlbumView.centerPageIndex + let hqURL = URL(string: items[currentIndex].imageUrlAbsolute)! + + if let op = loadOriginal { + op.cancel() + loadOriginal = nil + } + + let view2 = photoAlbumView.centerPageView() as! NIPhotoScrollView + + if view2.photoSize() != NIPhotoScrollViewPhotoSizeOriginal { + if nextImage != nil && currentIndex == nextImageIndex { + self.photoAlbumView.didLoadPhoto(nextImage!, at: currentIndex, photoSize: NIPhotoScrollViewPhotoSizeOriginal) + nextImage = nil + } + else { + let op2 = ImageLoadOperation(imageURL: hqURL, succeeder: { + i in + // self.photoAlbumView.isZoomingAboveOriginalSizeEnabled = max(i.size.height, i.size.width) <= 6000; + self.photoAlbumView.didLoadPhoto(i, at: currentIndex, photoSize: NIPhotoScrollViewPhotoSizeOriginal) + }, index: currentIndex) + loadOriginal = op2 + op2.qualityOfService = QualityOfService.userInitiated + operationQueue.addOperation(op2) + } + } + + if currentIndex + 1 < items.count { + if let op = loadNext { + op.cancel() + loadNext = nil + } + + if let image = thumbnails?.getThumbnail(items[currentIndex+1]) { + self.photoAlbumView.didLoadPhoto(image, at: currentIndex+1, photoSize: NIPhotoScrollViewPhotoSizeThumbnail) + } + + let hqURL2 = URL(string: items[currentIndex+1].imageUrlAbsolute)! + nextImage = nil + + let op3 = ImageLoadOperation(imageURL: hqURL2, succeeder: { + i in + self.nextImage = i + self.nextImageIndex = currentIndex + 1 + }, index: currentIndex) + loadNext = op3 + op3.qualityOfService = QualityOfService.userInitiated + operationQueue.addOperation(op3) + } + + let oldIndex = currentIndex - 1; + + if (oldIndex >= 0) { + let newItem = items[oldIndex] + if let image = thumbnails?.getThumbnail(newItem) { + self.photoAlbumView.didLoadPhoto(image, at: oldIndex, photoSize: NIPhotoScrollViewPhotoSizeThumbnail) + } + } + + } + func numberOfPages(in pagingScrollView: NIPagingScrollView) -> Int { let c = items.count return c @@ -432,7 +351,7 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView func pagingScrollView(_ pagingScrollView: NIPagingScrollView, pageViewFor pageViewForIndex: Int) -> (UIView & NIPagingScrollViewPageProtocol)! { let view = photoAlbumView.pagingScrollView(pagingScrollView, pageViewFor: pageViewForIndex) as! NIPhotoScrollView - view.maximumScale = 3; + // view.maximumScale = 2; return view; @@ -452,36 +371,15 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView if thumbnailIndex < 0 { return nil } - - var image: UIImage? - let newItem = items[thumbnailIndex] - let u1 = URL(string: newItem.thumbUrlAbsolute) - - if let URL = u1 { - - image = imageCache.object(forKey: URL.absoluteString as AnyObject) as? UIImage - - if (image == nil) { - urlSession!.dataTask(with: URL) { - (da, response, error) in - - if let d = da { - if let i = UIImage(data: d) { -// println("thumb image loaded \(newItem.thumbUrlAbsolute)") - let imageRef = i.cgImage; - self.imageCache.setObject(i, forKey: newItem.thumbUrlAbsolute as AnyObject, cost: d.count) - - self.photoScrubberView.didLoadThumbnail(i, at: thumbnailIndex); - } - } - } - } - } - else { - print("## Invalid URL: \(newItem.thumbUrlAbsolute)") + let thumbnail: (UIImage, Int) -> () = { (i: UIImage, thumbnailIndex: Int) in + self.photoScrubberView.didLoadThumbnail(i, at: thumbnailIndex) } + let image = thumbnails?.loadThumbnail(thumbnailIndex: thumbnailIndex, thumbnail: thumbnail) + return image } + + } diff --git a/kplayer/util/HanekeFetchOperation.swift b/kplayer/util/HanekeFetchOperation.swift index 56f4eea..f59184a 100644 --- a/kplayer/util/HanekeFetchOperation.swift +++ b/kplayer/util/HanekeFetchOperation.swift @@ -8,10 +8,10 @@ import Haneke class HanekeFetchOperation: Operation { let baseUrl: URL - let succeeder: Fetch.Succeeder + var succeeder: (UIImage) -> () let index: Int - init(baseUrl: URL, succeeder: @escaping Fetch.Succeeder, index: Int) { + init(baseUrl: URL, succeeder: @escaping (UIImage) -> (), index: Int) { self.baseUrl = baseUrl self.succeeder = succeeder self.index = index diff --git a/kplayer/util/ImageLoadOperation.swift b/kplayer/util/ImageLoadOperation.swift index da9fdd8..c9e3896 100644 --- a/kplayer/util/ImageLoadOperation.swift +++ b/kplayer/util/ImageLoadOperation.swift @@ -7,8 +7,8 @@ import Foundation import Alamofire import Darwin -class ImageLoadOperation: Operation { - internal typealias Succeeder = (UIImage) -> () +public class ImageLoadOperation: Operation { + public typealias Succeeder = (UIImage) -> () let imageURL: URL let succeeder: Succeeder @@ -17,51 +17,43 @@ class ImageLoadOperation: Operation { var manager: SessionManager? - var ready1 = false - - - init(imageURL: URL, succeeder: @escaping Succeeder, index: Int) { + public init(imageURL: URL, succeeder: @escaping Succeeder, index: Int) { self.imageURL = imageURL self.succeeder = succeeder self.index = index } - override func main() { + override public func main() { if self.isCancelled { return } - let r = URLRequest(url: imageURL) -// r.httpBody = nil -// r.addValue("0", forHTTPHeaderField: "Content-Length") - - if let m = manager { - request = m.request(r) - } else { - request = Alamofire.request(r) - } - - - request!.response { (response) in - if let e = response.error as? NSError { - if (e.code != -999) { - print(e) - } + print("lazy fetch image \(imageURL)") + if let data = try? Data(contentsOf: imageURL) { +// print("lazy fetch image ready \(imageURL)") + if self.isCancelled { + // print("Decoding 1 cancelled") + return } - else if let d = response.data { - self.succeeder(UIImage(data: d, scale: 1)!) + if let img = UIImage(data: data, scale: 1) { + if let decodedImg = img.decodeImage() { + if self.isCancelled { + // print("Decoding 2 cancelled") + return + } + DispatchQueue.main.async { + print("lazy fetch image decoded") + self.succeeder(decodedImg) + } + } } } - - while !ready1 { - usleep(10000) - } } - override func cancel() { -// println("cancel load \(imageURL.absoluteString!)") - - ready1 = true + override public func cancel() { +// if !self.isCancelled { +// print("cancel load \(imageURL)") +// } if let r = request { r.cancel() } diff --git a/kplayer/util/UIImageExtension.swift b/kplayer/util/UIImageExtension.swift index 46e1eef..c84d0c9 100644 --- a/kplayer/util/UIImageExtension.swift +++ b/kplayer/util/UIImageExtension.swift @@ -23,5 +23,19 @@ extension UIImage { return scaledImage!; } + func decodeImage() -> UIImage? { + guard let newImage = self.cgImage else { return nil } + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let context = CGContext(data: nil, width: newImage.width, height: newImage.height, bitsPerComponent: 8, bytesPerRow: newImage.width * 4, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) + + context?.draw(newImage, in: CGRect(x: 0, y: 0, width: newImage.width, height: newImage.height)) + let drawnImage = context?.makeImage() + + if let drawnImage = drawnImage { + return UIImage(cgImage: drawnImage) + } + return nil + } } diff --git a/kplayerTests/kplayerTests.swift b/kplayerTests/kplayerTests.swift index 9639b15..7ca6283 100644 --- a/kplayerTests/kplayerTests.swift +++ b/kplayerTests/kplayerTests.swift @@ -8,6 +8,7 @@ import UIKit import XCTest +import kplayer class kplayerTests: XCTestCase { @@ -23,10 +24,22 @@ class kplayerTests: XCTestCase { func testMyExample() { // http://linkstation.local/tomcat/media/service/listpicdirs/srv/samba/ren/series/sg + // http://linkstation:8080/tomcat/media/service/download/srv/samba/ren/heg/big/francy-made-in-italy-10000px/francy-made-in-italy-03-10000px.jpg + let hqURL = URL(string: + "http://linkstation:8080/tomcat/media/service/download/srv/samba/ren/heg/emily/emily-amazing-10000px/emily-amazing-21-10000px.jpg")! + // "http://linkstation:8080/tomcat/media/service/download/srv/samba/ren/heg/big/francy-made-in-italy-10000px/francy-made-in-italy-03-10000px.jpg")! + + let op = ImageLoadOperation(imageURL: hqURL, succeeder: { + i in +// println("image loaded \(newItem.imageUrlAbsolute) at \(photoAtIndex)") + print(i) + }, index: 1) + + op.main() print("hello") - let i = 12 + let i = 1 // This is an example of a functional test case. XCTAssert(i == 1, "Pass") } @@ -34,7 +47,11 @@ class kplayerTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. self.measure() { - // Put the code you want to measure the time of here. + let hqURL = URL(string: + "http://linkstation:8080/tomcat/media/service/download/srv/samba/ren/heg/emily/emily-amazing-10000px/emily-amazing-21-10000px.jpg")! + let data = try? Data(contentsOf: hqURL) + + print(data) } }