Fix port, begin upload UI

This commit is contained in:
Christoph Hagen
2021-01-10 16:11:31 +01:00
parent 75ebdf59ae
commit 746b69defc
11 changed files with 142 additions and 121 deletions

View File

@@ -128,13 +128,23 @@ final class Database {
var pendingCapUploads: [Cap] {
do {
return try db.prepare(Cap.table.filter(Cap.columnUploaded == false)).map(Cap.init)
return try db.prepare(Cap.table.filter(Cap.columnUploaded == false).order(Cap.columnId.asc)).map(Cap.init)
} catch {
log("Failed to get pending cap uploads")
return []
}
}
var hasPendingCapUploads: Bool {
do {
let query = Cap.table.filter(Cap.columnUploaded == false).count
return try db.scalar(query) > 0
} catch {
log("Failed to get pending cap upload count")
return false
}
}
var classifierVersion: Int {
set {
UserDefaults.standard.set(newValue, forKey: Classifier.userDefaultsKey)
@@ -409,24 +419,17 @@ final class Database {
// MARK: Downloads
@discardableResult
func downloadMainImage(for cap: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
return download.mainImage(for: cap) { success in
guard success else {
completion(false)
return
func downloadImage(for cap: Int, version: Int = 0, completion: @escaping (_ image: UIImage?) -> Void) -> Bool {
return download.image(for: cap, version: version) { image in
if version == 0 && image != nil {
DispatchQueue.main.async {
self.delegate?.database(didLoadImageForCap: cap)
}
}
DispatchQueue.main.async {
self.delegate?.database(didLoadImageForCap: cap)
}
completion(true)
completion(image)
}
}
@discardableResult
func downloadImage(for cap: Int, version: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
return download.image(for: cap, version: version, completion: completion)
}
func downloadCapNames(completion: @escaping (_ success: Bool) -> Void) {
log("Downloading cap names")
download.names { names in
@@ -496,7 +499,7 @@ final class Database {
DispatchQueue.global(qos: .userInitiated).async {
for part in caps.split(intoPartsOf: split) {
for id in part {
let downloading = self.downloadMainImage(for: id) { _ in
let downloading = self.downloadImage(for: id) { _ in
group.leave()
}
if downloading {
@@ -658,6 +661,24 @@ final class Database {
self.update(count: count, for: cap)
}
}
private func uploadNextItem() {
let capUploads = self.pendingCapUploads
if let id = capUploads.first {
return
}
let imageUploads = pendingImageUploads
guard imageUploads.count > 0 else {
log("No pending image uploads")
return
}
uploadRemainingImages()
}
private func upload(cap: Int) {
}
func uploadRemainingData() {
guard !isInOfflineMode else {
@@ -673,23 +694,33 @@ final class Database {
log("\(uploads.count) cap uploads pending")
var remaining = uploads.count
for cap in uploads {
upload.upload(name: cap.name, for: cap.id) { success in
if success {
self.log("Uploaded cap \(cap.id)")
self.update(uploaded: true, for: cap.id)
} else {
self.log("Failed to upload cap \(cap.id)")
}
remaining -= 1
if remaining == 0 {
DispatchQueue.main.async {
self.uploadRemainingImages()
DispatchQueue.global(qos: .background).async {
let group = DispatchGroup()
for cap in uploads {
group.enter()
self.upload.upload(name: cap.name, for: cap.id) { success in
group.leave()
if success {
self.log("Uploaded cap \(cap.id)")
self.update(uploaded: true, for: cap.id)
} else {
self.log("Failed to upload cap \(cap.id)")
return
}
remaining -= 1
}
guard group.wait(timeout: .now() + .seconds(60)) == .success else {
self.log("Timed out uploading cap \(cap.id)")
return
}
}
DispatchQueue.main.async {
self.uploadRemainingImages()
}
}
}
private func uploadRemainingImages() {
@@ -700,16 +731,35 @@ final class Database {
}
log("\(uploads.count) image uploads pending")
for (cap, version) in uploads {
upload.uploadImage(for: cap, version: version) { count in
guard let _ = count else {
self.log("Failed to upload version \(version) of cap \(cap)")
DispatchQueue.global(qos: .background).async {
let group = DispatchGroup()
for (id, version) in uploads {
guard let cap = self.cap(for: id) else {
self.log("No cap \(id) to upload image \(version)")
self.removePendingUpload(of: id, version: version)
continue
}
guard cap.uploaded else {
self.log("Cap \(id) not uploaded, skipping image upload")
continue
}
group.enter()
self.upload.uploadImage(for: id, version: version) { count in
group.leave()
guard let _ = count else {
self.log("Failed to upload version \(version) of cap \(id)")
return
}
self.log("Uploaded version \(version) of cap \(id)")
self.removePendingUpload(of: id, version: version)
}
guard group.wait(timeout: .now() + .seconds(60)) == .success else {
self.log("Timed out uploading version \(version) of cap \(id)")
return
}
self.log("Uploaded version \(version) of cap \(cap)")
self.removePendingUpload(of: cap, version: version)
}
}
}
@discardableResult

View File

@@ -116,59 +116,35 @@ final class Download {
- Parameter cap: The id of the cap.
- Parameter version: The image version to download.
- Parameter completion: A closure with the resulting image
- Note: The closure will be called from the main queue.
- Returns: `true`, of the file download was started, `false`, if the image is already downloading.
*/
@discardableResult
func mainImage(for cap: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
let url = serverImageUrl(for: cap)
let query = "Main image of cap \(cap)"
guard !downloadingMainImages.contains(cap) else {
return false
func image(for cap: Int, version: Int = 0, completion: @escaping (_ image: UIImage?) -> Void) -> Bool {
// Check if main image, and already being downloaded
if version == 0 {
guard !downloadingMainImages.contains(cap) else {
return false
}
downloadingMainImages.insert(cap)
}
downloadingMainImages.insert(cap)
let task = session.downloadTask(with: url) { fileUrl, response, error in
DispatchQueue.main.async {
self.downloadingMainImages.remove(cap)
}
guard let fileUrl = self.convertResponse(to: query, fileUrl, response, error) else {
completion(false)
return
}
guard app.storage.saveImage(at: fileUrl, for: cap) else {
self.log("Request '\(query)' could not move downloaded file")
completion(false)
return
}
completion(true)
}
task.resume()
return true
}
/**
Download an image for a cap.
- Parameter cap: The id of the cap.
- Parameter version: The image version to download.
- Parameter completion: A closure with the resulting image
- Returns: `true`, of the file download was started, `false`, if the image is already downloading.
*/
@discardableResult
func image(for cap: Int, version: Int, completion: @escaping (_ success: Bool) -> Void) -> Bool {
let url = serverImageUrl(for: cap, version: version)
let query = "Image of cap \(cap) version \(version)"
let task = session.downloadTask(with: url) { fileUrl, response, error in
if version == 0 {
DispatchQueue.main.async {
self.downloadingMainImages.remove(cap)
}
}
guard let fileUrl = self.convertResponse(to: query, fileUrl, response, error) else {
completion(false)
completion(nil)
return
}
guard app.storage.saveImage(at: fileUrl, for: cap, version: version) else {
guard let image = app.storage.saveImage(at: fileUrl, for: cap, version: version) else {
self.log("Request '\(query)' could not move downloaded file")
completion(false)
completion(nil)
return
}
completion(true)
completion(image)
}
task.resume()
return true

View File

@@ -56,17 +56,17 @@ final class Storage {
- parameter version: The version of the image to get
- returns: True, if the image was saved
*/
func saveImage(at url: URL, for cap: Int, version: Int = 0) -> Bool {
func saveImage(at url: URL, for cap: Int, version: Int = 0) -> UIImage? {
let targetUrl = localImageUrl(for: cap, version: version)
do {
if fm.fileExists(atPath: targetUrl.path) {
try fm.removeItem(at: targetUrl)
}
try fm.moveItem(at: url, to: targetUrl)
return true
return UIImage(contentsOfFile: targetUrl.path)
} catch {
log("Failed to delete or move image \(version) for cap \(cap)")
return false
return nil
}
}