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.
224 lines
7.4 KiB
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
|
|
}
|
|
|
|
}
|
|
}
|
|
|