Загрузка видео на 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)
                    }
            })
        }

    }

}
Другие вопросы по тегам