Загрузка видео на Youtube с использованием API REST без iOS SDK
Все еще новичок, так терпите меня. Я использую SWIFT 3 и API данных V3 YouTube с REST. Я могу вытащить список моих видео, чтобы мое подключение и авторизация работали просто отлично.
Я не могу понять, как загрузить, хотя. Я нашел старый пост, который был очень похож на мой ( Настройка данных фрагмента для загрузки на YouTube через REST API с помощью Swift).
Я запутался, откуда они получают эту переменную токена и как они передают ее в эту функцию. Кроме того, не уверен, как установить переменную загрузки, которая находится прямо перед публикацией. Любая помощь приветствуется!
func uploadVideo(token: String, callback: @escaping (Bool) -> Void){
let headers = ["Authorization": "Bearer \(token)"]
let path = Bundle.main.path(forResource: "intro", ofType: "mov")
let videodata: NSData = NSData.dataWithContentsOfMappedFile(path!)! as! NSData
upload(
.POST,
"https://www.googleapis.com/upload/youtube/v3/videos?part=snippet",
headers: headers,
multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(data:"{'snippet':{'title' : 'TITLE_TEXT', 'description': 'DESCRIPTION_TEXT'}}".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"snippet", mimeType: "application/json")
multipartFormData.appendBodyPart(data: videodata, name: "intro", fileName: "intro.mov", mimeType: "application/octet-stream")
},
encodingCompletion: { encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.responseJSON { request, response, error in
print(response)
callback(true)
}
case .Failure(_):
callback(false)
}
})
}
1 ответ
Обновленный код с https://github.com/mfriedl89/Developer5/blob/master/Conari/Conari/. В основном это просто документация, требуется рефакторинг (например: Codable
для анализа JSON вместо принудительного развертывания). И правильный поток OAuth2 должен быть реализован с использованием SFSafariViewController
/SFAuthenticationSession
(в зависимости от целевой версии iOS) или Google SignIn
import Foundation
class YoutubeTokenProvider {
// TODO: Store token, evaluate ttl, retry request if needed
static func getAccessToken(callback: @escaping (String?) -> Void){
/* Remark (Security)
Storing the data inside here is not secure, but still better than requesting it from a custom request.
Two possibilities:
1. Storing all data here
2. Storing all data on an external server and only requesting the access token from this server
Option 1 was chosen based on the assumption that decompiling the app should be more difficult than just
monitoring the request and getting the access token (which allows the attacker to do anything with our
YouTube account). The disadvantage is that an attacker gets everything after a successful decompilation
and not only the access token.
*/
let client_secret = "..."
let grant_type = "refresh_token"
let refresh_token = "..."
let client_id = "..."
var request = URLRequest(url: URL(string: "https://accounts.google.com/o/oauth2/token")!)
request.httpMethod = "POST"
let postString = "client_secret=" + client_secret +
"&grant_type=" + grant_type +
"&refresh_token=" + refresh_token +
"&client_id=" + client_id
request.httpBody = postString.data(using: .utf8)
URLSession.shared.dataTask(with: request, completionHandler: {data, response, error in
guard let data = data, error == nil else {
callback(nil)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
callback(nil)
return
}
do {
let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! YouTubeManager.JSON
let accessToken = jsonData["access_token"]! as? String
callback(accessToken)
} catch {
callback(nil)
}
return
}).resume()
}
}
import Foundation
struct YoutubeVideo {
let title, thumbnailUrl, videoId: String
}
import Foundation
import Alamofire
/// This file will act as our YouTube manager.
class YouTubeManager {
typealias JSON = [String:AnyObject]
typealias SearchByTitleCallback = (_ response: [YoutubeVideo], _ success:Bool, _ message:String) -> Void
typealias PostVideoCallback = (String, Bool) -> Void
/** Singletone instance. */
static let sharedManager = YouTubeManager()
let apiKey = "..."
let channelID = "..."
let searchApiUrl = "https://www.googleapis.com/youtube/v3/search"
let identifier = "videoID: "
func parseIdentifier(input: String) -> String? {
let seperator = "videoID: "
if input.contains(self.identifier) {
let videoID = input.components(separatedBy: seperator)
return videoID.last
}
return nil
}
func searchVideoByTitle(title: String, completionHandler: @escaping SearchByTitleCallback) -> Void {
let eTitle = title.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
let urlString = searchApiUrl + "?part=snippet&q=" + eTitle + "&type=video&key=" + apiKey
let targetURL = URL(string: urlString)!
var returnArray = [YoutubeVideo]()
let task = URLSession.shared.dataTask(with: targetURL) { (data, response, error) in
guard error == nil else {
completionHandler(returnArray, false, "error = \(error!)")
return
}
guard let data = data else {
completionHandler(returnArray, false, "error = data is nil")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
completionHandler(returnArray, false, "response = \(response!)")
return
}
do {
let resultsDict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! JSON
let items = resultsDict["items"] as! [JSON]
returnArray = items.map { item in
let snippetDict = item["snippet"] as! JSON
let title = snippetDict["title"] as! String
let thumbnail = ((snippetDict["thumbnails"] as! JSON)["default"] as! JSON)["url"] as! String
let videoid = (item["id"] as! JSON)["videoId"] as! String
return YoutubeVideo(title: title, thumbnailUrl: thumbnail, videoId: videoid)
}
completionHandler(returnArray, true, "")
} catch {
completionHandler(returnArray, false, "error serializing JSON: \(error)")
}
}
task.resume()
}
func postVideoToYouTube(uploadUrl: String, videoData: Data, title: String, callback: @escaping PostVideoCallback){
YoutubeTokenProvider.getAccessToken { (accessToken) in
guard let accessToken = accessToken
else { return }
let headers: HTTPHeaders = ["Authorization": "Bearer \(accessToken)"]
Alamofire.upload(
multipartFormData: { multipartFormData in
let metadata = "{'snippet':{'title' : '\(title)', 'description': 'This video was uploaded using Mr Tutor.'}}".data(using: .utf8, allowLossyConversion: false)!
multipartFormData.append(metadata, withName: "snippet", mimeType: "application/json")
multipartFormData.append(videoData, withName: "video", fileName: "sample.mp4", mimeType: "application/octet-stream")
},
to: "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet",
headers: headers,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
do {
let jsonData = try JSONSerialization.jsonObject(with: response.data!, options: .allowFragments) as! JSON
let videoID = jsonData["id"] as! String
let identifierFinal = self.identifier + videoID
callback(identifierFinal, true)
} catch {
print("error serializing JSON: \(error)")
callback("", false)
}
print("Success")
}
case .failure(_):
print("Failure")
callback("", false)
}
})
}
}
}