【问题标题】:iOS Swift: How to post a video to twitter with TwitterKit?iOS Swift:如何使用 TwitterKit 将视频发布到 Twitter?
【发布时间】:2016-07-14 22:08:49
【问题描述】:

我还没有找到关于 SO 的相关且最新的答案。

这是我目前正在使用的代码,所有请求都得到了正确处理,但没有发布视频?

if let userID = Twitter.sharedInstance().sessionStore.session()?.userID {
    var client = TWTRAPIClient(userID: userID)

    let text: String = "Testing Video"
    let videoLength: String = "\(self.video.length)"
    print(videoLength)
    var initError: NSError?
    var message = ["status": text, "command" : "INIT", "media_type" : "video/m4v", "total_bytes" : videoLength]
    let preparedRequest: NSURLRequest = client.URLRequestWithMethod("POST", URL: self.strUploadUrl, parameters: message, error: &initError)
    client.sendTwitterRequest(preparedRequest, completion: { (urlResponse: NSURLResponse?, responseData: NSData?, error: NSError?) -> Void in
        if error == nil {

            do {


                let json: NSDictionary = try (NSJSONSerialization.JSONObjectWithData(responseData!, options: NSJSONReadingOptions(rawValue: 0)) as? NSDictionary)!
                print("JSON is \(json)")


                let mediaID = json.objectForKey("media_id_string") as! String

                client = TWTRAPIClient(userID: userID)
                var uploadError: NSError?
                let videoString = self.video.base64EncodedStringWithOptions([])

                message = ["command" : "APPEND", "media_id" : mediaID, "segment_index" : "0", "media" : videoString]
                let preparedRequest = client.URLRequestWithMethod("POST", URL: self.strUploadUrl, parameters: message, error: &uploadError)
                client.sendTwitterRequest(preparedRequest, completion: { (urlResponse: NSURLResponse?, responseData: NSData?, error: NSError?) -> Void in
                    if error == nil {
                        client = TWTRAPIClient(userID: userID)
                        var finalizeError: NSError?
                        message = ["command":"FINALIZE", "media_id": mediaID]
                        let preparedRequest = client.URLRequestWithMethod("POST", URL: self.strUploadUrl, parameters: message, error: &finalizeError)
                        client.sendTwitterRequest(preparedRequest, completion: { (urlResponse: NSURLResponse?, responseData: NSData?, error: NSError?) -> Void in
                            if error == nil {
                                client = TWTRAPIClient(userID: userID)
                                var sendError: NSError?
                                let message = ["status": text, "wrap_links": "true", "media_ids": mediaID]
                                //var updateMessage = NSMutableDictionary(dictionary: message)
                                let preparedRequest = client.URLRequestWithMethod("POST", URL: self.strStatusUrl, parameters: message , error: &sendError)
                                client.sendTwitterRequest(preparedRequest, completion: { (urlResponse: NSURLResponse?, responseData: NSData?, error: NSError?) -> Void in

                                })
                            } else {
                                print("Command FINALIZE failed \n \(error!)")
                            }
                        })
                    } else {
                        print("Command APPEND failed")
                    }
                })
            }

            catch {
                print("\(error)")
            }
        }

        else {
            print("\(error.debugDescription)Command INIT failed")
        }
    })
    }

上面的所有代码都可以工作,除了视频没有上传。我不知道我错过了什么,而且 Twitter 的文档在发布视频方面非常糟糕。

【问题讨论】:

    标签: ios swift video twitter


    【解决方案1】:

    如果您想将@Trung 的代码转换为 Swift 4,并且如果您想使用 TwitterKit,您可以使用这个枚举来处理所有上传阶段:

    enum UploadStage {
        case initial(size: String, videoDuration: Int?) // if your video duration is <= 30s, you can pass nil here
        case append(mediaId: String, videoData: Data, segment: Int)
        case finalize(mediaId: String)
        case status(status: String, mediaId: String)
    
        static let videoChunkMaxSize = 5 * 1000 * 1000
    
        var parameters: [String: Any] {
            get {
                switch self {
    
                case .initial(let size, let videoDuration):
                    var params = ["command":stageName, "total_bytes": size, "media_type": "video/mp4"]
                    if let videoDuration = videoDuration, videoDuration > 30 {
                        params["media_category"] = "tweet_video"
                    }
                    return params
                case .append(let mediaId, _ , let segment):
                    let videoChunkString = self.videoChunk!.base64EncodedString(options: [])
                    return ["command":stageName, "media_id": mediaId, "segment_index": "\(segment)", "media": videoChunkString]
                case .finalize(let mediaId):
                    return ["command":stageName, "media_id": mediaId]
                case .status(let status, let mediaId):
                    return ["status": status, "wrap_links": "true", "media_ids": mediaId]
                }
            }
        }
    
        var stageName: String {
            get {
                switch self {
                case .initial:
                    return "INIT"
                case .append:
                    return "APPEND"
                case .finalize:
                    return "FINALIZE"
                case .status:
                    return "STATUS"
    
                }
            }
        }
    
        var videoChunk: Data? {
            switch self {
            case .append(_ , let videoData, let segment):
                if videoData.count > UploadStage.videoChunkMaxSize {
                    let maxPos = segment * UploadStage.videoChunkMaxSize + UploadStage.videoChunkMaxSize
                    let range: Range<Data.Index> = segment * UploadStage.videoChunkMaxSize..<(maxPos >= videoData.count ? videoData.count : maxPos)
                    return videoData.subdata(in: range)
    
                }
                return videoData
            default:
                return nil
            }
        }
    
        var urlString: String {
            switch self {
            case .initial, .append, .finalize:
                return "https://upload.twitter.com/1.1/media/upload.json"
            case .status:
                return "https://api.twitter.com/1.1/statuses/update.json"
            }
        }
    }
    

    UploadStage 枚举可以在递归调用的方法中使用,方法是像这样传递下一个阶段枚举值:

    func uploadTwitterVideo(videoData: Data, status: String, stage: UploadStage, success: @escaping () -> Void, failure: @escaping (Error?) -> Void) {
    
        let client = TWTRAPIClient.withCurrentUser()
    
        var clientError: NSError?
        let urlRequest = client.urlRequest(withMethod: "POST", urlString: stage.urlString, parameters: stage.parameters, error: &clientError)
        if clientError == nil {
            client.sendTwitterRequest(urlRequest) { (urlResponse, responseData, connectionError) in
    
                guard connectionError == nil else {
                    print("There was an error: \(connectionError!.localizedDescription)")
                    failure(connectionError)
                    return
                }
    
                self.handleError(urlResponse, failure: failure)
                if let data = responseData, let dataString = String(data: data, encoding: .utf8), let urlResponse = urlResponse {
                    print("Twitter stage \(stage.stageName) URL response : \(urlResponse), response data: \(dataString)")
    
    
                    var nextStage: UploadStage?
                    do {
                        switch stage {
                        case .initial:
                            let returnedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String:Any]
                            if let mediaId = returnedJSON["media_id_string"] as? String {
                                print("stage one success, mediaID -> \(mediaId)")
                                nextStage = .append(mediaId: mediaId, videoData:videoData, segment: 0)
                            }
                        case .append(let mediaId, let videoData, let segment):
                            if ((segment + 1) * UploadStage.videoChunkMaxSize < videoData.count) {
                                nextStage = .append(mediaId: mediaId, videoData: videoData, segment: segment + 1)
                            } else {
                                nextStage = .finalize(mediaId: mediaId)
                            }
                        case .finalize(let mediaId):
                            nextStage = .status(status: status, mediaId: mediaId)
                        case .status:
                            success()
                        }
    
                        if let nextStage = nextStage {
                            self.uploadTwitterVideo(videoData: data, status: status, stage: nextStage, success: success, failure: failure)
                        }
                    } catch let error as NSError {
                        failure(error)
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 这是一个极好的实现,我第一次尝试时效果很好。一件事,您需要将您的 connectionError 防护从 if-let 中提取出来以获取响应数据。我收到了关于长视频的错误,但没有响应数据,所以在我取消错误检查之前,错误从未出现过。我在 Twitter 文档中读到同步上传的最大视频长度为 30,但异步上传的最大视频长度为 140 秒,但使用此异步方法上传超过 30 秒的视频时,我继续收到 400 个错误(与 TWTRComposerViewController 抛出的错误相同)。关于如何达到 140 秒限制的任何想法?
    • 找到它:文档说“约束可能因 media_category 参数而异”,但很难找到超出此范围的指导。基本上,对于大于 30 秒的视频,您必须将“media_category”:“tweet_video”添加到 INIT 参数,然后在完成阶段之后,您必须重复最后一个阶段的请求,直到它给您一个视频的响应加工完毕。如果它还没有准备好,您将不会收到任何响应并出现 400 错误。然后,您可以通过最后阶段继续完成推文。 jwz.org/blog/2016/06/…
    • @JoelCave,感谢您的发言。我已经更新了我的答案。现在 connectionError 防护在 if let 之前移动。我的示例是持续时间
    • “UploadStage”类型的值没有成员“urlString”
    • 感谢@ShahbazAkram。我已经用 urlString 更新了替换的 url 变量。现在应该可以正常工作了。
    【解决方案2】:

    对于 Twitter 糟糕的文档,我深表同情。 找出你得到了什么错误。

    以下是实现说明,希望对您有所帮助:

    1. Twitter 视频要求: https://dev.twitter.com/rest/public/uploading-media#videorecs
    2. FINALIZE 命令根据 Twitter 视频要求验证视频文件 在完成上传之前。
    3. 如果您收到“HTTP 状态 400 错误请求”,响应数据包含“无效或不受支持的媒体,原因:UnsupportedMedia”。发送 FINALIZE 命令后出错,您需要验证您的视频文件是否符合 Twitter 视频要求。

    查看我的项目https://github.com/mtrung/TwitterVideoUpload。我知道它在 Obj-C 中,但它可以工作。

    【讨论】:

      猜你喜欢
      • 2015-04-24
      • 2017-02-08
      • 1970-01-01
      • 2015-10-23
      • 2015-09-03
      • 2019-12-16
      • 2013-12-04
      • 2015-10-12
      • 1970-01-01
      相关资源
      最近更新 更多