|
|
@ -37,7 +37,7 @@ class GiphyAssetSegment {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// * During downloads it will be accessed on the task delegate queue.
|
|
|
|
// * During downloads it will be accessed on the task delegate queue.
|
|
|
|
// * After downloads it will be accessed on a worker queue.
|
|
|
|
// * After downloads it will be accessed on a worker queue.
|
|
|
|
private var segmentData = NSMutableData()
|
|
|
|
private var segmentData = Data()
|
|
|
|
|
|
|
|
|
|
|
|
init(index: UInt,
|
|
|
|
init(index: UInt,
|
|
|
|
segmentStart: UInt,
|
|
|
|
segmentStart: UInt,
|
|
|
@ -50,7 +50,7 @@ class GiphyAssetSegment {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public func totalDataSize() -> UInt {
|
|
|
|
public func totalDataSize() -> UInt {
|
|
|
|
return UInt(segmentData.length)
|
|
|
|
return UInt(segmentData.count)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public func append(data: Data) {
|
|
|
|
public func append(data: Data) {
|
|
|
@ -62,13 +62,13 @@ class GiphyAssetSegment {
|
|
|
|
segmentData.append(data)
|
|
|
|
segmentData.append(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public func mergeData(assetData: NSMutableData) -> Bool {
|
|
|
|
public func mergeData(assetData: inout Data) -> Bool {
|
|
|
|
guard state == .complete else {
|
|
|
|
guard state == .complete else {
|
|
|
|
owsFail("\(TAG) merging data in invalid state: \(state)")
|
|
|
|
owsFail("\(TAG) merging data in invalid state: \(state)")
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard UInt(segmentData.length) == segmentLength else {
|
|
|
|
guard UInt(segmentData.count) == segmentLength else {
|
|
|
|
owsFail("\(TAG) segment data length: \(segmentData.length) doesn't match expected length: \(segmentLength)")
|
|
|
|
owsFail("\(TAG) segment data length: \(segmentData.count) doesn't match expected length: \(segmentLength)")
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -77,11 +77,11 @@ class GiphyAssetSegment {
|
|
|
|
// tail of the segment data.
|
|
|
|
// tail of the segment data.
|
|
|
|
let bytesToIgnore = Int(redundantLength)
|
|
|
|
let bytesToIgnore = Int(redundantLength)
|
|
|
|
if bytesToIgnore > 0 {
|
|
|
|
if bytesToIgnore > 0 {
|
|
|
|
let range = NSMakeRange(bytesToIgnore, segmentData.length - bytesToIgnore)
|
|
|
|
let range = NSMakeRange(bytesToIgnore, segmentData.count - bytesToIgnore)
|
|
|
|
let subdata = (segmentData as NSData).subdata(with: range)
|
|
|
|
let subdata = segmentData.subdata(in: range.location..<range.location + range.length)
|
|
|
|
assetData.append(subdata)
|
|
|
|
assetData.append(subdata)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
assetData.append(segmentData as Data)
|
|
|
|
assetData.append(segmentData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -248,7 +248,7 @@ enum GiphyAssetRequestState: UInt {
|
|
|
|
|
|
|
|
|
|
|
|
public func writeAssetToFile(gifFolderPath: String) -> GiphyAsset? {
|
|
|
|
public func writeAssetToFile(gifFolderPath: String) -> GiphyAsset? {
|
|
|
|
|
|
|
|
|
|
|
|
let assetData = NSMutableData()
|
|
|
|
var assetData = Data()
|
|
|
|
for segment in segments {
|
|
|
|
for segment in segments {
|
|
|
|
guard segment.state == .complete else {
|
|
|
|
guard segment.state == .complete else {
|
|
|
|
owsFail("\(TAG) unexpected incomplete segment.")
|
|
|
|
owsFail("\(TAG) unexpected incomplete segment.")
|
|
|
@ -258,18 +258,18 @@ enum GiphyAssetRequestState: UInt {
|
|
|
|
owsFail("\(TAG) could not merge empty segment.")
|
|
|
|
owsFail("\(TAG) could not merge empty segment.")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard segment.mergeData(assetData: assetData) else {
|
|
|
|
guard segment.mergeData(assetData: &assetData) else {
|
|
|
|
owsFail("\(TAG) failed to merge segment data.")
|
|
|
|
owsFail("\(TAG) failed to merge segment data.")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
guard assetData.length == contentLength else {
|
|
|
|
guard assetData.count == contentLength else {
|
|
|
|
owsFail("\(TAG) asset data has unexpected length.")
|
|
|
|
owsFail("\(TAG) asset data has unexpected length.")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
guard assetData.length > 0 else {
|
|
|
|
guard assetData.count > 0 else {
|
|
|
|
owsFail("\(TAG) could not write empty asset to disk.")
|
|
|
|
owsFail("\(TAG) could not write empty asset to disk.")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -280,13 +280,14 @@ enum GiphyAssetRequestState: UInt {
|
|
|
|
|
|
|
|
|
|
|
|
Logger.verbose("\(TAG) filePath: \(filePath).")
|
|
|
|
Logger.verbose("\(TAG) filePath: \(filePath).")
|
|
|
|
|
|
|
|
|
|
|
|
let success = assetData.write(toFile: filePath, atomically: true)
|
|
|
|
do {
|
|
|
|
guard success else {
|
|
|
|
try assetData.write(to: NSURL.fileURL(withPath:filePath), options: .atomicWrite)
|
|
|
|
owsFail("\(TAG) could not write asset to disk.")
|
|
|
|
let asset = GiphyAsset(rendition: rendition, filePath : filePath)
|
|
|
|
|
|
|
|
return asset
|
|
|
|
|
|
|
|
} catch let error as NSError {
|
|
|
|
|
|
|
|
owsFail("\(GiphyAsset.TAG) file write failed: \(filePath), \(error)")
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let asset = GiphyAsset(rendition: rendition, filePath : filePath)
|
|
|
|
|
|
|
|
return asset
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public func cancel() {
|
|
|
|
public func cancel() {
|
|
|
@ -546,7 +547,6 @@ extension URLSessionTask {
|
|
|
|
self.assetMap.set(key:assetRequest.rendition.url, value:asset)
|
|
|
|
self.assetMap.set(key:assetRequest.rendition.url, value:asset)
|
|
|
|
self.removeAssetRequestFromQueue(assetRequest:assetRequest)
|
|
|
|
self.removeAssetRequestFromQueue(assetRequest:assetRequest)
|
|
|
|
assetRequest.requestDidSucceed(asset:asset)
|
|
|
|
assetRequest.requestDidSucceed(asset:asset)
|
|
|
|
self.processRequestQueue()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -566,7 +566,6 @@ extension URLSessionTask {
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
self.removeAssetRequestFromQueue(assetRequest:assetRequest)
|
|
|
|
self.removeAssetRequestFromQueue(assetRequest:assetRequest)
|
|
|
|
assetRequest.requestDidFail()
|
|
|
|
assetRequest.requestDidFail()
|
|
|
|
self.processRequestQueue()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -588,35 +587,35 @@ extension URLSessionTask {
|
|
|
|
private func processRequestQueue() {
|
|
|
|
private func processRequestQueue() {
|
|
|
|
AssertIsOnMainThread()
|
|
|
|
AssertIsOnMainThread()
|
|
|
|
|
|
|
|
|
|
|
|
guard let assetRequest = self.popNextAssetRequest() else {
|
|
|
|
guard let assetRequest = popNextAssetRequest() else {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard !assetRequest.wasCancelled else {
|
|
|
|
guard !assetRequest.wasCancelled else {
|
|
|
|
// Discard the cancelled asset request and try again.
|
|
|
|
// Discard the cancelled asset request and try again.
|
|
|
|
self.removeAssetRequestFromQueue(assetRequest: assetRequest)
|
|
|
|
removeAssetRequestFromQueue(assetRequest: assetRequest)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard UIApplication.shared.applicationState == .active else {
|
|
|
|
guard UIApplication.shared.applicationState == .active else {
|
|
|
|
// If app is not active, fail the asset request.
|
|
|
|
// If app is not active, fail the asset request.
|
|
|
|
assetRequest.state = .failed
|
|
|
|
assetRequest.state = .failed
|
|
|
|
self.assetRequestDidFail(assetRequest:assetRequest)
|
|
|
|
assetRequestDidFail(assetRequest:assetRequest)
|
|
|
|
self.processRequestQueue()
|
|
|
|
processRequestQueue()
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if let asset = self.assetMap.get(key:assetRequest.rendition.url) {
|
|
|
|
if let asset = assetMap.get(key:assetRequest.rendition.url) {
|
|
|
|
// Deferred cache hit, avoids re-downloading assets that were
|
|
|
|
// Deferred cache hit, avoids re-downloading assets that were
|
|
|
|
// downloaded while this request was queued.
|
|
|
|
// downloaded while this request was queued.
|
|
|
|
|
|
|
|
|
|
|
|
assetRequest.state = .complete
|
|
|
|
assetRequest.state = .complete
|
|
|
|
self.assetRequestDidSucceed(assetRequest : assetRequest, asset: asset)
|
|
|
|
assetRequestDidSucceed(assetRequest : assetRequest, asset: asset)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
guard let downloadSession = self.giphyDownloadSession() else {
|
|
|
|
guard let downloadSession = giphyDownloadSession() else {
|
|
|
|
owsFail("\(self.TAG) Couldn't create session manager.")
|
|
|
|
owsFail("\(TAG) Couldn't create session manager.")
|
|
|
|
assetRequest.state = .failed
|
|
|
|
assetRequest.state = .failed
|
|
|
|
self.assetRequestDidFail(assetRequest:assetRequest)
|
|
|
|
assetRequestDidFail(assetRequest:assetRequest)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -629,10 +628,8 @@ extension URLSessionTask {
|
|
|
|
request.httpMethod = "HEAD"
|
|
|
|
request.httpMethod = "HEAD"
|
|
|
|
|
|
|
|
|
|
|
|
let task = downloadSession.dataTask(with:request, completionHandler: { [weak self] data, response, error -> Void in
|
|
|
|
let task = downloadSession.dataTask(with:request, completionHandler: { [weak self] data, response, error -> Void in
|
|
|
|
if let data = data {
|
|
|
|
if let data = data, data.count > 0 {
|
|
|
|
if data.count > 0 {
|
|
|
|
owsFail("\(self?.TAG) HEAD request has unexpected body: \(data.count).")
|
|
|
|
owsFail("\(self?.TAG) HEAD request has unexpected body: \(data.count).")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self?.handleAssetSizeResponse(assetRequest:assetRequest, response:response, error:error)
|
|
|
|
self?.handleAssetSizeResponse(assetRequest:assetRequest, response:response, error:error)
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -642,7 +639,7 @@ extension URLSessionTask {
|
|
|
|
// Start a download task.
|
|
|
|
// Start a download task.
|
|
|
|
|
|
|
|
|
|
|
|
guard let assetSegment = assetRequest.firstWaitingSegment() else {
|
|
|
|
guard let assetSegment = assetRequest.firstWaitingSegment() else {
|
|
|
|
owsFail("\(self.TAG) queued asset request does not have a waiting segment.")
|
|
|
|
owsFail("\(TAG) queued asset request does not have a waiting segment.")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assetSegment.state = .downloading
|
|
|
|
assetSegment.state = .downloading
|
|
|
@ -657,7 +654,7 @@ extension URLSessionTask {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Recurse; we may be able to start multiple downloads.
|
|
|
|
// Recurse; we may be able to start multiple downloads.
|
|
|
|
self.processRequestQueue()
|
|
|
|
processRequestQueue()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func handleAssetSizeResponse(assetRequest: GiphyAssetRequest, response: URLResponse?, error: Error?) {
|
|
|
|
private func handleAssetSizeResponse(assetRequest: GiphyAssetRequest, response: URLResponse?, error: Error?) {
|
|
|
|