Tusker/Pachyderm/Model/Attachment.swift

138 lines
4.5 KiB
Swift

//
// Attachment.swift
// Pachyderm
//
// Created by Shadowfacts on 9/9/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import Foundation
public class Attachment: Decodable {
public let id: String
public let kind: Kind
public let url: URL
public let remoteURL: URL?
public let previewURL: URL
public let textURL: URL?
public let meta: Metadata?
public let description: String?
public static func update(_ attachment: Attachment, focus: (Float, Float)?, description: String?) -> Request<Attachment> {
return Request<Attachment>(method: .put, path: "/api/v1/media/\(attachment.id)", body: .formData([
"description" => (description ?? attachment.description),
"focus" => focus
], nil))
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.kind = try container.decode(Kind.self, forKey: .kind)
self.url = URL(lenient: try container.decode(String.self, forKey: .url))!
if let remote = try? container.decode(String.self, forKey: .remoteURL) {
self.remoteURL = URL(lenient: remote.replacingOccurrences(of: " ", with: "%20"))
} else {
self.remoteURL = nil
}
self.previewURL = URL(lenient: try container.decode(String.self, forKey: .previewURL).replacingOccurrences(of: " ", with: "%20"))!
if let text = try? container.decode(String.self, forKey: .textURL) {
self.textURL = URL(lenient: text.replacingOccurrences(of: " ", with: "%20"))
} else {
self.textURL = nil
}
self.meta = try? container.decode(Metadata.self, forKey: .meta)
self.description = try? container.decode(String.self, forKey: .description)
}
private enum CodingKeys: String, CodingKey {
case id
case kind = "type"
case url
case remoteURL = "remote_url"
case previewURL = "preview_url"
case textURL = "text_url"
case meta
case description
}
}
extension Attachment {
public enum Kind: String, Decodable {
case image
case video
case gifv
case audio
case unknown
// we need a custom decoder, because all API-compatible implementations don't return some data in the same format
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let str = try container.decode(String.self)
if let kind = Kind(rawValue: str.lowercased()) {
self = kind
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Attachment type must be one of image, video, gifv, audio, or unknown.")
}
}
}
}
extension Attachment {
public class Metadata: Decodable {
public let length: String?
public let duration: Float?
public let audioEncoding: String?
public let audioBitrate: String?
public let audioChannels: String?
public let fps: Float?
public let width: Int?
public let height: Int?
public let size: String?
public let aspect: Float?
public let small: ImageMetadata?
public let original: ImageMetadata?
private enum CodingKeys: String, CodingKey {
case length
case duration
case audioEncoding = "audio_encode"
case audioBitrate = "audio_bitrate"
case audioChannels = "audio_channels"
case fps
case width
case height
case size
case aspect
case small
case original
}
}
public class ImageMetadata: Decodable {
public let width: Int?
public let height: Int?
public let size: String?
public let aspect: Float?
private enum CodingKeys: String, CodingKey {
case width
case height
case size
case aspect
}
}
}
fileprivate extension URL {
private static let allowedChars = CharacterSet.urlHostAllowed.union(.urlPathAllowed).union(.urlQueryAllowed)
init?(lenient string: String) {
guard let escaped = string.addingPercentEncoding(withAllowedCharacters: URL.allowedChars) else {
return nil
}
self.init(string: escaped)
}
}