Browse Source

Swift 4.2

master
Marco Schmickler 7 years ago
parent
commit
387afeadd9
  1. 4
      kplayer.xcodeproj/project.pbxproj
  2. 60
      kplayer/core/NetworkManager.swift
  3. 187
      kplayer/core/ThumbnailCache.swift
  4. 55
      kplayer/detail/DetailViewController.swift
  5. 300
      kplayer/photo/MediaPhotoController.swift
  6. 4
      kplayer/util/HanekeFetchOperation.swift
  7. 52
      kplayer/util/ImageLoadOperation.swift
  8. 14
      kplayer/util/UIImageExtension.swift
  9. 21
      kplayerTests/kplayerTests.swift

4
kplayer.xcodeproj/project.pbxproj

@ -16,6 +16,7 @@
1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; }; 1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; };
1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; }; 1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; };
1C7365885FAF292F2221ED44 /* MediaPhotoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73673DC671535E3A049F54 /* MediaPhotoController.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 */; }; 1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; };
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; }; 1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; };
1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73648CEC974A2500172064 /* ViewControllerExtensions.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 = "<group>"; }; 1C736D981F8315FFD7D40695 /* KBMPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KBMPlayer.swift; sourceTree = "<group>"; };
1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = "<group>"; }; 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = "<group>"; };
1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; }; 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = "<group>"; };
1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailCache.swift; sourceTree = "<group>"; };
1C736F9338CE36708244D42A /* DataLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataLoadOperation.swift; sourceTree = "<group>"; }; 1C736F9338CE36708244D42A /* DataLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataLoadOperation.swift; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
8B75159FFCD5A882E6F167FE /* Pods_kplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_kplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 */, 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */,
1C73688DAB88F9360FB62A49 /* MediaItem.swift */, 1C73688DAB88F9360FB62A49 /* MediaItem.swift */,
1C736253AB7A95EA41B605B7 /* ItemModel.swift */, 1C736253AB7A95EA41B605B7 /* ItemModel.swift */,
1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */,
); );
path = core; path = core;
sourceTree = "<group>"; sourceTree = "<group>";
@ -447,6 +450,7 @@
1C7369763AB6C73472E11B55 /* KBMPlayer.swift in Sources */, 1C7369763AB6C73472E11B55 /* KBMPlayer.swift in Sources */,
1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */, 1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */,
1C7362A6FA1C5DA0B0677F1E /* readme.md in Sources */, 1C7362A6FA1C5DA0B0677F1E /* readme.md in Sources */,
1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

60
kplayer/core/NetworkManager.swift

@ -46,17 +46,11 @@ class NetworkManager {
print("Empfange \(json.count) Ergebnisse") print("Empfange \(json.count) Ergebnisse")
for s in json { for s in json {
// print(s)
if s.hasSuffix(".mp4") || s.hasSuffix(".m4v") { if s.hasSuffix(".mp4") || s.hasSuffix(".m4v") {
let l = s.count let l = s.count
let name = NSURL(fileURLWithPath: s).lastPathComponent! let name = NSURL(fileURLWithPath: s).lastPathComponent!
var pathlen = l - len - name.count var pathlen = l - len - name.count
// if (pathlen > 1000) {
// print(pathlen)
// print(name)
// print(s)
// }
if (pathlen < 2) { if (pathlen < 2) {
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) { func favItem(_ item: MediaItem) {
let url = baseurl + "/service/linkfav" + item.fullPath 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)
}
}
}
} }

187
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<AnyObject, AnyObject>()
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<UInt8>(&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
}
}

55
kplayer/detail/DetailViewController.swift

@ -185,8 +185,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
let ms = Int(t * 1000) let ms = Int(t * 1000)
let p = c.snapshotDirPathForVideo + "\(ms).jpg" let p = c.snapshotDirPathForVideo + "\(ms).jpg"
let pt = c.snapshotDirPathForVideo + "\(ms)_thumb.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() self.collectionView.reloadData()
} }
@ -254,7 +255,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
headerView.setItem(items) headerView.setItem(items)
return headerView return headerView
default: default:
assert(false, "Unexpected element kind")
fatalError("Unexpected element kind")
} }
} }
@ -276,53 +277,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
showVideo() showVideo()
} }
else { 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)
}
}
}
NetworkManager.sharedInstance.loadDetails(items: items, result: { (im: [MediaItem]) in
self.showPhotos(im) self.showPhotos(im)
}
}
})
} }
} }
} }

300
kplayer/photo/MediaPhotoController.swift

@ -7,32 +7,37 @@ import Foundation
import Nimbus import Nimbus
import Alamofire 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 items = [MediaItem]()
var completionHandler: (() -> Void)? var completionHandler: (() -> Void)?
var requests = Array<ImageLoadOperation>()
var imageCache = NSCache<AnyObject, AnyObject>()
var total = 0
var loadOriginal : ImageLoadOperation?
var loadNext : ImageLoadOperation?
var nextImage : UIImage?
var nextImageIndex = 0
var slide = 0 var slide = 0
var timer: Timer? var timer: Timer?
var currentIndex = 80
var urlSession: Foundation.URLSession?
var sessionOwner = false
let dateFormatter = DateFormatter()
var thumbnails: ThumbnailCache?
lazy var operationQueue: OperationQueue = { lazy var operationQueue: OperationQueue = {
var queue = OperationQueue() var queue = OperationQueue()
queue.name = "Photo queue" queue.name = "Photo queue"
queue.maxConcurrentOperationCount = 3
queue.maxConcurrentOperationCount = 1
return queue return queue
}() }()
@ -46,24 +51,9 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
title = "(\(idx + 1) / \(photoAlbumView.numberOfPages)) \(selected.path) \(selected.name)" title = "(\(idx + 1) / \(photoAlbumView.numberOfPages)) \(selected.path) \(selected.name)"
} }
open func URLSession(_ session: Foundation.URLSession, didBecomeInvalidWithError error: NSError?) {
print(error)
}
override func viewDidLoad() { override func viewDidLoad() {
super.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 backButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(MediaPhotoController.back))
let slideButton = UIBarButtonItem(barButtonSystemItem: .fastForward, target: self, action: #selector(MediaPhotoController.slideShow)) let slideButton = UIBarButtonItem(barButtonSystemItem: .fastForward, target: self, action: #selector(MediaPhotoController.slideShow))
navigationItem.leftBarButtonItems = [backButton, slideButton] navigationItem.leftBarButtonItems = [backButton, slideButton]
@ -74,110 +64,19 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
setChromeVisibility(true, animated: true) 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: {
thumbnails = ThumbnailCache(items: items, refresh: {
() -> Void in () -> Void in
self.photoScrubberView.reloadData(); 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<UInt8>(&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)")
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() { override func didReceiveMemoryWarning() {
print("warning") print("warning")
imageCache.removeAllObjects()
thumbnails?.cancel()
super.didReceiveMemoryWarning() super.didReceiveMemoryWarning()
} }
@ -196,12 +95,9 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
} }
@objc func back() { @objc func back() {
imageCache.removeAllObjects()
operationQueue.cancelAllOperations() operationQueue.cancelAllOperations()
if sessionOwner {
urlSession!.invalidateAndCancel()
}
thumbnails?.cancel();
completionHandler!() completionHandler!()
} }
@ -286,8 +182,6 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
} }
let pc = MediaPhotoController() let pc = MediaPhotoController()
pc.urlSession = self.urlSession!
pc.items = im pc.items = im
pc.completionHandler = { pc.completionHandler = {
self.dismiss(animated: true, completion: nil); self.dismiss(animated: true, completion: nil);
@ -351,73 +245,98 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
var size = NIPhotoScrollViewPhotoSizeUnknown 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
}
image = thumbnails?.getThumbnail(newItem)
if image != nil {
size = NIPhotoScrollViewPhotoSizeThumbnail
isLoading[0] = false
} else {
let thumbnail: (UIImage, Int) -> () = { (i: UIImage, thumbnailIndex: Int) in
self.photoAlbumView.didLoadPhoto(i, at: photoAtIndex, photoSize: NIPhotoScrollViewPhotoSizeThumbnail)
self.photoScrubberView.didLoadThumbnail(i, at: thumbnailIndex)
} }
if !ok {
r.cancel()
requests.remove(at: requests.index(of: r)!)
let image = thumbnails?.loadThumbnail(thumbnailIndex: photoAtIndex, thumbnail: thumbnail)
} }
if (image == nil) {
isLoading[0] = true
} }
image = imageCache.object(forKey: u5.absoluteString as AnyObject) as? UIImage
photoSize[0] = size
if image != nil {
size = NIPhotoScrollViewPhotoSizeThumbnail
isLoading[0] = false
return image
} else { } 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);
print("## Invalid URL: \(newItem.imageUrlAbsolute)")
return nil
} }
} }
}, index: photoAtIndex, session: urlSession!)
op3.qualityOfService = QualityOfService.userInteractive
operationQueue.addOperation(op3)
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: { let op2 = ImageLoadOperation(imageURL: hqURL, succeeder: {
i in i in
// println("image loaded \(newItem.imageUrlAbsolute) at \(photoAtIndex)")
size = NIPhotoScrollViewPhotoSizeOriginal
self.photoAlbumView.didLoadPhoto(i, at: photoAtIndex, photoSize: size)
photoSize[0] = size
}, index: photoAtIndex)
// 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 op2.qualityOfService = QualityOfService.userInitiated
requests.append(op2)
operationQueue.addOperation(op2) operationQueue.addOperation(op2)
}
}
if (image == nil) {
isLoading[0] = true
if currentIndex + 1 < items.count {
if let op = loadNext {
op.cancel()
loadNext = nil
} }
photoSize[0] = size
if let image = thumbnails?.getThumbnail(items[currentIndex+1]) {
self.photoAlbumView.didLoadPhoto(image, at: currentIndex+1, photoSize: NIPhotoScrollViewPhotoSizeThumbnail)
}
return image
} else {
print("## Invalid URL: \(newItem.imageUrlAbsolute)")
let hqURL2 = URL(string: items[currentIndex+1].imageUrlAbsolute)!
nextImage = nil
return 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 { func numberOfPages(in pagingScrollView: NIPagingScrollView) -> Int {
let c = items.count let c = items.count
return c return c
@ -432,7 +351,7 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
func pagingScrollView(_ pagingScrollView: NIPagingScrollView, pageViewFor pageViewForIndex: Int) -> (UIView & NIPagingScrollViewPageProtocol)! { func pagingScrollView(_ pagingScrollView: NIPagingScrollView, pageViewFor pageViewForIndex: Int) -> (UIView & NIPagingScrollViewPageProtocol)! {
let view = photoAlbumView.pagingScrollView(pagingScrollView, pageViewFor: pageViewForIndex) as! NIPhotoScrollView let view = photoAlbumView.pagingScrollView(pagingScrollView, pageViewFor: pageViewForIndex) as! NIPhotoScrollView
view.maximumScale = 3;
// view.maximumScale = 2;
return view; return view;
@ -452,36 +371,15 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
if thumbnailIndex < 0 { if thumbnailIndex < 0 {
return nil 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 return image
} }
} }

4
kplayer/util/HanekeFetchOperation.swift

@ -8,10 +8,10 @@ import Haneke
class HanekeFetchOperation: Operation { class HanekeFetchOperation: Operation {
let baseUrl: URL let baseUrl: URL
let succeeder: Fetch<UIImage>.Succeeder
var succeeder: (UIImage) -> ()
let index: Int let index: Int
init(baseUrl: URL, succeeder: @escaping Fetch<UIImage>.Succeeder, index: Int) {
init(baseUrl: URL, succeeder: @escaping (UIImage) -> (), index: Int) {
self.baseUrl = baseUrl self.baseUrl = baseUrl
self.succeeder = succeeder self.succeeder = succeeder
self.index = index self.index = index

52
kplayer/util/ImageLoadOperation.swift

@ -7,8 +7,8 @@ import Foundation
import Alamofire import Alamofire
import Darwin import Darwin
class ImageLoadOperation: Operation {
internal typealias Succeeder = (UIImage) -> ()
public class ImageLoadOperation: Operation {
public typealias Succeeder = (UIImage) -> ()
let imageURL: URL let imageURL: URL
let succeeder: Succeeder let succeeder: Succeeder
@ -17,51 +17,43 @@ class ImageLoadOperation: Operation {
var manager: SessionManager? 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.imageURL = imageURL
self.succeeder = succeeder self.succeeder = succeeder
self.index = index self.index = index
} }
override func main() {
override public func main() {
if self.isCancelled { if self.isCancelled {
return 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)
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
} }
request!.response { (response) in
if let e = response.error as? NSError {
if (e.code != -999) {
print(e)
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)
} }
else if let d = response.data {
self.succeeder(UIImage(data: d, scale: 1)!)
} }
} }
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 { if let r = request {
r.cancel() r.cancel()
} }

14
kplayer/util/UIImageExtension.swift

@ -23,5 +23,19 @@ extension UIImage {
return scaledImage!; 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
}
} }

21
kplayerTests/kplayerTests.swift

@ -8,6 +8,7 @@
import UIKit import UIKit
import XCTest import XCTest
import kplayer
class kplayerTests: XCTestCase { class kplayerTests: XCTestCase {
@ -23,10 +24,22 @@ class kplayerTests: XCTestCase {
func testMyExample() { func testMyExample() {
// http://linkstation.local/tomcat/media/service/listpicdirs/srv/samba/ren/series/sg // 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") print("hello")
let i = 12
let i = 1
// This is an example of a functional test case. // This is an example of a functional test case.
XCTAssert(i == 1, "Pass") XCTAssert(i == 1, "Pass")
} }
@ -34,7 +47,11 @@ class kplayerTests: XCTestCase {
func testPerformanceExample() { func testPerformanceExample() {
// This is an example of a performance test case. // This is an example of a performance test case.
self.measure() { 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)
} }
} }

Loading…
Cancel
Save