diff --git a/Sources/Uploadcare/Constants.swift b/Sources/Uploadcare/Constants.swift index 553d5e87..50dc2294 100644 --- a/Sources/Uploadcare/Constants.swift +++ b/Sources/Uploadcare/Constants.swift @@ -19,7 +19,7 @@ internal let RESTAPIHost = "api.uploadcare.com" /// Library name internal let libraryName = "UploadcareSwift" /// Library version -internal let libraryVersion = "0.9.0" +internal let libraryVersion = "0.9.1" /// API version internal let APIVersion = "0.7" diff --git a/Sources/Uploadcare/models/REST/ContentInfo.swift b/Sources/Uploadcare/models/REST/ContentInfo.swift index 09a09a82..2c508a32 100644 --- a/Sources/Uploadcare/models/REST/ContentInfo.swift +++ b/Sources/Uploadcare/models/REST/ContentInfo.swift @@ -11,10 +11,184 @@ import Foundation public struct ContentInfo: Codable { /// MIME type. public let mime: Mime? - + /// Image metadata. public let image: ImageInfo? /// Video metadata. - public let video: VideoInfo? + public let video: Video? +} + +extension ContentInfo { + /// Video metadata. + public struct Video: Codable, CustomDebugStringConvertible { + /// Video duration in milliseconds. + public var duration: Int + + /// Video format(MP4 for example). + public var format: String + + /// Video bitrate. + public var bitrate: Int + + /// Audio stream metadata. + public var audio: [AudioMetadata]? + + /// Video stream metadata. + public var video: [VideoMetadata] + + + enum CodingKeys: String, CodingKey { + case duration + case format + case bitrate + case audio + case video + } + + + init( + duration: Int, + format: String, + bitrate: Int, + audio: [AudioMetadata]?, + video: [VideoMetadata] + ) { + self.duration = duration + self.format = format + self.bitrate = bitrate + self.audio = audio + self.video = video + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let duration = try container.decodeIfPresent(Int.self, forKey: .duration) ?? 0 + let format = try container.decodeIfPresent(String.self, forKey: .format) ?? "" + let bitrate = try container.decodeIfPresent(Int.self, forKey: .bitrate) ?? 0 + let audio = try container.decodeIfPresent([AudioMetadata].self, forKey: .audio) + let video = try container.decodeIfPresent([VideoMetadata].self, forKey: .video) ?? [VideoMetadata(height: 0, width: 0, frameRate: 0, bitrate: 0, codec: "")] + + self.init( + duration: duration, + format: format, + bitrate: bitrate, + audio: audio, + video: video + ) + } + + public var debugDescription: String { + return """ + \(type(of: self)): + duration: \(duration) + format: \(format) + bitrate: \(bitrate) + audio: \(String(describing: audio)) + video: \(video) + """ + } + } +} + + +extension ContentInfo { + /// Video stream metadata. + public struct VideoMetadata: Codable, CustomDebugStringConvertible { + /// Video stream image height. + public var height: Int + /// Video stream image width. + public var width: Int + /// Video stream frame rate. + public var frameRate: Int + /// Video stream bitrate. + public var bitrate: Int? + /// Video stream codec. + public var codec: String? + + + enum CodingKeys: String, CodingKey { + case height + case width + case frameRate = "frame_rate" + case bitrate + case codec + } + + init( + height: Int, + width: Int, + frameRate: Int, + bitrate: Int?, + codec: String? + ) { + self.height = height + self.width = width + self.frameRate = frameRate + self.bitrate = bitrate + self.codec = codec + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let height = try container.decodeIfPresent(Int.self, forKey: .height) ?? 0 + let width = try container.decodeIfPresent(Int.self, forKey: .width) ?? 0 + let frameRate = try container.decodeIfPresent(Int.self, forKey: .frameRate) ?? 0 + let bitrate = try container.decodeIfPresent(Int.self, forKey: .bitrate) + let codec = try container.decodeIfPresent(String.self, forKey: .codec) + + self.init( + height: height, + width: width, + frameRate: frameRate, + bitrate: bitrate, + codec: codec + ) + } + + public var debugDescription: String { + return """ + \(type(of: self)): + height: \(height) + width: \(width) + frameRate: \(frameRate) + bitrate: \(bitrate as Any) + codec: \(codec as Any) + """ + } + } +} + + +extension ContentInfo { + /// Audio stream metadata. + public struct Audio: Codable, CustomDebugStringConvertible { + /// Audio stream's bitrate. + public var bitrate: Int? + /// Audio stream's codec. + public var codec: String? + /// Audio stream's sample rate. + public var sampleRate: Int? + /// Audio stream's number of channels. + public var channels: String? + + enum CodingKeys: String, CodingKey { + case bitrate + case codec + case sampleRate = "sample_rate" + case channels + } + + public var debugDescription: String { + return """ + \(type(of: self)): + bitrate: \(String(describing: bitrate)) + codec: \(String(describing: codec)) + sampleRate: \(String(describing: sampleRate)) + channels: \(String(describing: channels)) + """ + } + } } diff --git a/Sources/Uploadcare/models/VideoInfo.swift b/Sources/Uploadcare/models/VideoInfo.swift index 3a10a188..68e83b71 100644 --- a/Sources/Uploadcare/models/VideoInfo.swift +++ b/Sources/Uploadcare/models/VideoInfo.swift @@ -21,10 +21,10 @@ public struct VideoInfo: Codable { public var bitrate: Int /// Audio stream metadata. - public var audio: [AudioMetadata] - + public var audio: Audio? + /// Video stream metadata. - public var video: [VideoMetadata] + public var video: VideoMetadata enum CodingKeys: String, CodingKey { @@ -40,8 +40,8 @@ public struct VideoInfo: Codable { duration: Int, format: String, bitrate: Int, - audio: [AudioMetadata], - video: [VideoMetadata] + audio: Audio?, + video: VideoMetadata ) { self.duration = duration self.format = format @@ -56,8 +56,8 @@ public struct VideoInfo: Codable { let duration = try container.decodeIfPresent(Int.self, forKey: .duration) ?? 0 let format = try container.decodeIfPresent(String.self, forKey: .format) ?? "" let bitrate = try container.decodeIfPresent(Int.self, forKey: .bitrate) ?? 0 - let audio = try container.decodeIfPresent([AudioMetadata].self, forKey: .audio) ?? [AudioMetadata()] - let video = try container.decodeIfPresent([VideoMetadata].self, forKey: .video) ?? [VideoMetadata(height: 0, width: 0, frameRate: 0, bitrate: 0, codec: "")] + let audio = try container.decodeIfPresent(Audio.self, forKey: .audio) + let video = try container.decodeIfPresent(VideoMetadata.self, forKey: .video) ?? VideoMetadata(height: 0, width: 0, frameRate: 0, bitrate: 0, codec: "") self.init( duration: duration, @@ -83,3 +83,40 @@ extension VideoInfo: CustomDebugStringConvertible { """ } } + + +extension VideoInfo { + /// Audio stream metadata. + public struct Audio: Codable, CustomDebugStringConvertible { + /// Audio bitrate. + public var bitrate: Int? + + /// Audio stream codec. + public var codec: String? + + /// Audio stream sample rate. + public var sampleRate: Int? + + /// Audio stream number of channels. + public var channels: String? + + + enum CodingKeys: String, CodingKey { + case bitrate + case codec + case sampleRate = "sample_rate" + case channels + } + + public var debugDescription: String { + return """ + \(type(of: self)): + bitrate: \(String(describing: bitrate)) + codec: \(String(describing: codec)) + sampleRate: \(String(describing: sampleRate)) + channels: \(String(describing: channels)) + """ + } + } + +} diff --git a/Tests/UploadcareTests/RESTAPIIntegrationTests.swift b/Tests/UploadcareTests/RESTAPIIntegrationTests.swift index 3a395ed4..5bf07ee6 100644 --- a/Tests/UploadcareTests/RESTAPIIntegrationTests.swift +++ b/Tests/UploadcareTests/RESTAPIIntegrationTests.swift @@ -666,7 +666,7 @@ final class RESTAPIIntegrationTests: XCTestCase { XCTFail(error.detail) expectation.fulfill() case .success(let list): - let videoFile = list.results.first(where: { $0.mimeType == "video/mp4" })! + let videoFile = list.results.first(where: { $0.mimeType == "video/mp4" || $0.mimeType == "video/quicktime" })! let convertSettings = VideoConversionJobSettings(forFile: videoFile) .format(.webm) diff --git a/Tests/UploadcareTests/UploadAPIIntegrationTests.swift b/Tests/UploadcareTests/UploadAPIIntegrationTests.swift index de54c5b0..ac035b26 100644 --- a/Tests/UploadcareTests/UploadAPIIntegrationTests.swift +++ b/Tests/UploadcareTests/UploadAPIIntegrationTests.swift @@ -415,6 +415,31 @@ final class UploadAPIIntegrationTests: XCTestCase { wait(for: [expectation], timeout: 120.0) } + + func test13_multipartUpload_videoFile() { + let url = URL(string: "https://ucarecdn.com/3e8a90e7-f5ce-422e-a3ed-5eee952f9f3b/")! + let data = try! Data(contentsOf: url) + + let expectation = XCTestExpectation(description: "test13_multipartUpload_videoFile") + + let onProgress: (Double)->Void = { (progress) in + DLog("progress: \(progress)") + } + + let metadata = ["multipart": "upload"] + + uploadcare.uploadAPI.multipartUpload(data, withName: "video.MP4", store: .doNotStore, metadata: metadata, onProgress) { result in + defer { expectation.fulfill() } + + switch result { + case .failure(let error): + XCTFail(error.detail) + case .success(_): + break + } + } + wait(for: [expectation], timeout: 180.0) + } } #endif diff --git a/Uploadcare.podspec b/Uploadcare.podspec index a2aa70be..11c4c65b 100644 --- a/Uploadcare.podspec +++ b/Uploadcare.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Uploadcare' - s.version = '0.9.0' + s.version = '0.9.1' s.summary = 'Swift integration for Uploadcare' # This description is used to generate tags and improve search results.