Uploading video to Youtube using its REST API without the iOS SDK

夙愿已清 提交于 2020-01-17 12:24:05

问题


Still a noob, so bear with me. I am using SWIFT 3 and the V3 YouTube Data API with REST. I can pull a list of my videos so my connection and authorization is working just fine.

I can't seem to figure out how to upload though. I found an old post that was very similar to mine (Setting snippet data for youtube upload via REST API using Swift).

I'm confused where they are getting that token variable from and how they pass it into this function. Also, not sure how to set the upload variable that is right before the post. Any help is appreciated!

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:


Updated code from https://github.com/mfriedl89/Developer5/blob/master/Conari/Conari/. It's just a poc mostly, refactoring required (e.g: Use Codable for JSON parsing instead of force-unwrapping). And a proper OAuth2 flow should be implemented using SFSafariViewController/SFAuthenticationSession (depending on the targeted iOS version) or 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)
                    }
            })
        }

    }

}


来源:https://stackoverflow.com/questions/45800480/uploading-video-to-youtube-using-its-rest-api-without-the-ios-sdk

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!