You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Utils/CloudConvert.swift

224 lines
7.4 KiB

//
// CloudConvert.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 14/09/2023.
//
import Foundation
class CloudConvert {
enum CloudConvertionError: LocalizedError {
case unknownError
case serviceError(ErrorResponse)
case urlNotFound(String)
var errorDescription: String? {
switch self {
case .unknownError:
return "Erreur"
case .serviceError(let errorResponse):
return errorResponse.error
case .urlNotFound(let url):
return "L'URL [\(url)] n'est pas valide"
}
}
}
static let manager = CloudConvert()
func uploadFile(_ url: URL) async throws -> String {
let taskResponse = try await createJob(url)
let uploadResponse = try await uploadFile(taskResponse, url: url)
var fileReady = false
while fileReady == false {
try await Task.sleep(nanoseconds: 3_000_000_000)
let progressResponse = try await checkFile(id: uploadResponse.data.id)
if progressResponse.data.step == "finish" && progressResponse.data.stepPercent == 100 {
fileReady = true
print("progressResponse.data.minutes", progressResponse.data.minutes)
}
}
let convertedFile = try await downloadConvertedFile(id: uploadResponse.data.id)
return convertedFile
}
func createJob(_ url: URL) async throws -> TaskResponse {
guard let taskURL = URL(string: "https://api.convertio.co/convert") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert")
}
var request: URLRequest = URLRequest(url: taskURL)
let parameters = """
{"apikey":"d97cf13ef6d163e5e386c381fc8d256f","input":"upload","file":"","filename":"","outputformat":"csv","options":""}
"""
let postData = parameters.data(using: .utf8)
request.httpMethod = "POST"
request.httpBody = postData
let task = try await URLSession.shared.data(for: request)
//print("tried: \(request.url)")
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: task.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
return try JSONDecoder().decode(TaskResponse.self, from: task.0)
}
func uploadFile(_ response: TaskResponse, url: URL) async throws -> UploadResponse {
guard let uploadTaskURL = URL(string: "https://api.convertio.co/convert/\(response.data.id)/\(url.encodedLastPathComponent)") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(response.data.id)/\(url.encodedLastPathComponent)")
}
var uploadRequest: URLRequest = URLRequest(url: uploadTaskURL)
uploadRequest.httpMethod = "PUT"
let uploadTask = try await URLSession.shared.upload(for: uploadRequest, fromFile: url)
//print("tried: \(uploadRequest.url)")
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: uploadTask.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
return try JSONDecoder().decode(UploadResponse.self, from: uploadTask.0)
}
func checkFile(id: String) async throws -> ProgressResponse {
guard let taskURL = URL(string: "https://api.convertio.co/convert/\(id)/status") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(id)/status")
}
var request: URLRequest = URLRequest(url: taskURL)
request.httpMethod = "GET"
let task = try await URLSession.shared.data(for: request)
//print("tried: \(request.url)")
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: task.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
return try JSONDecoder().decode(ProgressResponse.self, from: task.0)
}
func downloadConvertedFile(id: String) async throws -> String {
// try await Task.sleep(nanoseconds: 3_000_000_000)
guard let downloadTaskURL = URL(string: "https://api.convertio.co/convert/\(id)/dl/base64") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(id)/dl/base64")
}
var downloadRequest: URLRequest = URLRequest(url: downloadTaskURL)
downloadRequest.httpMethod = "GET"
let downloadTask = try await URLSession.shared.data(for: downloadRequest)
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: downloadTask.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
//print("tried: \(downloadRequest.url)")
let dataResponse = try JSONDecoder().decode(DataResponse.self, from: downloadTask.0)
if let decodedData = Data(base64Encoded: dataResponse.data.content), let string = String(data: decodedData, encoding: .utf8) {
return string
}
throw CloudConvertionError.unknownError
}
}
// MARK: - DataResponse
struct DataResponse: Decodable {
let code: Int
let status: String
let data: DataDownloadClass
}
// MARK: - DataClass
struct DataDownloadClass: Decodable {
let id, encode, content: String
}
// MARK: - ErrorResponse
struct ErrorResponse: Decodable {
let code: Int
let status, error: String
}
// MARK: - TaskResponse
struct TaskResponse: Decodable {
let code: Int
let status: String
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Decodable {
let id: String
}
// MARK: - ProgressResponse
struct ProgressResponse: Decodable {
let code: Int
let status: String
let data: ProgressDataClass
}
// MARK: - DataClass
struct ProgressDataClass: Decodable {
let id, step: String
let stepPercent: Int
let minutes: String
enum CodingKeys: String, CodingKey {
case id, step
case stepPercent = "step_percent"
case minutes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
step = try container.decode(String.self, forKey: .step)
minutes = try container.decode(String.self, forKey: .minutes)
if let value = try? container.decode(String.self, forKey: .stepPercent) {
print(value)
stepPercent = Int(value) ?? 0
} else {
stepPercent = try container.decode(Int.self, forKey: .stepPercent)
}
}
}
// MARK: - Output
struct Output: Decodable {
let url: String
let size: String
}
// MARK: - UploadResponse
struct UploadResponse: Decodable {
let code: Int
let status: String
let data: UploadDataClass
}
// MARK: - DataClass
struct UploadDataClass: Decodable {
let id, file: String
let size: Int
}
extension URL {
var encodedLastPathComponent: String {
if #available(iOS 17.0, *) {
lastPathComponent
} else {
lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? lastPathComponent
}
}
}