Twitter video upload fail on Android

restapi
media-upload
video

#1

I am trying to upload a video to twitter on Android.

Every time I use the API, I get a HTTP 400 (with no error message), on the APPEND command.

My request looks like this (using httpbin)

HEADERS

User-Agent: okhttp/3.2.0
Content-Length: 4000810
Total-Route-Time: 0
Accept-Encoding: gzip
Cf-Ipcountry: JP
Connection: close
Authorization: OAuth oauth_consumer_key="[Redacted]", oauth_nonce="[Redacted]", oauth_signature="[Redacted]%2BkY9kybcE%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1476240797", oauth_token="-[Redacted]", oauth_version="1.0"
Host: requestb.in
Content-Type: multipart/form-data; boundary=81302723-6d1b-4c0b-898a-ca52dd2aef10
Cf-Connecting-Ip: 118.238.220.243
X-Request-Id: e68c732e-5b59-4671-823a-f2ef1aa4e5c7
Via: 1.1 vegur
Cf-Visitor: {"scheme":"http"} => https for the real one
Connect-Time: 0
Cf-Ray: 2f0742ba47e0132f-NRT
RAW BODY

--81302723-6d1b-4c0b-898a-ca52dd2aef10
Content-Disposition: form-data; name="command"
Content-Transfer-Encoding: binary
Content-Type: application/json; charset=UTF-8
Content-Length: 8

"APPEND"
--81302723-6d1b-4c0b-898a-ca52dd2aef10
Content-Disposition: form-data; name="media_id"
Content-Transfer-Encoding: binary
Content-Type: application/json; charset=UTF-8
Content-Length: 20

"[]"
--81302723-6d1b-4c0b-898a-ca52dd2aef10
Content-Disposition: form-data; name="media"
Content-Transfer-Encoding: binary
Content-Type: video/avc
[Redacted binary gibberish]

The error I get while using the API is:

Response
{
protocol=h2,
code=400,
message=,
url=https://upload.twitter.com/1.1/media/upload.json
}

The final file is 6mb; and ffmpeg -i on the file yeild the result

Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: isommp42
creation_time : 2016-10-12 02:21:19
com.android.version: 6.0 Duration: 00:00:22.12, start: 0.000000, bitrate: 2387 kb/s
Stream #0:0(eng): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 480x480, 1135 kb/s, SAR 1:1 DAR 1:1, 25 fps, 25
tbr, 90k tbn, 50 tbc (default)
Metadata:
creation_time : 2016-10-12 02:21:19
handler_name : VideoHandle

The code to handle the upload looks like this:

class MPTwitterApiClient extends TwitterApiClient {
    public MPTwitterApiClient(TwitterSession session) {
        super(session);
    }

    /**
     * Provide CustomService with defined endpoints
     */
    public VideoService getVideoService() {
        return getService(VideoService.class);
    }

}

// example users/show service endpoint
interface VideoService {


    @FormUrlEncoded()
    @POST("https://upload.twitter.com/1.1/media/upload.json")
    Call<VideoUploadInit> uploadVideoInit(@Field("command") String command,
                                 @Field("total_bytes") String totalBytes,
                                 @Field("media_type") String mediaType);
    @Multipart
    @POST("https://upload.twitter.com/1.1/media/upload.json")
    Call <VideoUploadPart>uploadVideoAppend(@Part("command") String command,
                           @Part("media_id") String mediaId,
                           @Part("media") RequestBody media, // The raw binary file content being uploaded. Cannot be used with media_data.
                           // Required after an INIT, an index number starting at zero indicating the order of the uploaded chunks.
                           // The chunk of the upload for a single media, from 0-999, inclusive.
                           // The first segment_index is 0, the second segment uploaded is 1, etc.
                           @Part("segment_index") String segmentIndex);

    @POST("https://upload.twitter.com/1.1/media/upload.json")
      @FormUrlEncoded()
    Call<VideoUploadEnd>  uploadVideoFinalize(@Field("command") String command,
                             @Field("media_id") long mediaId);
    public class VideoUploadInit {

        @SerializedName("media_id")
        public final long mediaId;

        public VideoUploadInit(final long pMediaId) {
            mediaId = pMediaId;
        }

    }

    public class VideoUploadPart {

    }

    public class VideoUploadEnd {

    }
}

And the upload code:

 private void uploadChunk(final VideoService videoService, final byte data[], final long mediaId , final int fileSize, final int chunkPart) {
        final int maxChunk = 4 * 1000 * 1000;
        final int byteSent = chunkPart * maxChunk;
        final boolean isLast = byteSent + maxChunk >= fileSize;
        RequestBody body = new RequestBody() {
            @Override
            public MediaType contentType() {
                return MediaType.parse("video/mp4");
            }

            @Override
            public void writeTo(final BufferedSink sink) throws IOException {
                sink.write(data, byteSent, isLast ? fileSize - byteSent : maxChunk);
            }
        };
        videoService.uploadVideoAppend("APPEND", String.valueOf(mediaId), body, String.valueOf(chunkPart)).enqueue(new Callback<VideoService.VideoUploadPart>() {
            @Override
            public void success(final Result result) {

                Log.d(TAG, "Uploaded video part");
                if (isLast) {
                    videoService.uploadVideoFinalize("FINALIZE", mediaId).enqueue(new Callback<VideoService.VideoUploadEnd>() {
                        @Override
                        public void success(final Result<VideoService.VideoUploadEnd> result) {
                            Log.e(TAG, "Finalized upload !");
                        }

                        @Override
                        public void failure(final TwitterException exception) {
                            Log.e(TAG, "Failed upload finalization");
                        }
                    });
                } else {
                    uploadChunk(videoService, data, mediaId, fileSize, chunkPart + 1);
                }
            }

            @Override
            public void failure(final TwitterException exception) {
                Log.e(TAG, "Could not upload video: " + exception);
            }
        });
    }


    private void tweet(TwitterSession pTwitterSession) {
        TwitterAuthToken authToken = pTwitterSession.getAuthToken();
        String token = authToken.token;
        String secret = authToken.secret;
        MPTwitterApiClient twitterApiClient = new MPTwitterApiClient(pTwitterSession);
        final VideoService videoService = twitterApiClient.getVideoService();
        final File videoFile = new File(AssetsUtils.getExportMoviePath(getApplicationContext()));
        Log.d(TAG, "File Size is " + (videoFile.length() / 1024 / 1024) + "mb");
        try {
            final byte data[] = Files.toByteArray(videoFile);
            videoService.uploadVideoInit("INIT", String.valueOf(videoFile.length()), "video/mp4").enqueue(new Callback<VideoService.VideoUploadInit>() { //XXX refactor this callback hell
                @Override
                public void success(final Result<VideoService.VideoUploadInit> result) {
                    Log.d(TAG, "Succeed INIT RESULT: " +result);
                    Log.d(TAG, "media ID is " + result.data.mediaId);
                    final long mediaId = result.data.mediaId;
                    final int fileSize = (int)videoFile.length();
                    uploadChunk(videoService, data, mediaId, fileSize, 0);
                    }
                @Override
                public void failure(final TwitterException exception) {
                    Log.d(TAG, "Failed twitter init with " + exception);
                }
            });
        } catch (IOException pE) {
            pE.printStackTrace();
        }
    }

The media INIT call run successfully and return a media id. The append part fail on first chunk.

(Yes, it’s all white because the content is still under NDA).


#2

Hello ntnmrndn,

Did you solve your problem? I have the same issue, APPEND request failed with status error 400, bad request.

Thanks.


#3

Having been in the same situation, and being tired of those answers that say “I fixed it” without taking the time to explain how, i’m reviving this thread to reveal the solution.

I fixed the problem by making a RequestBody for each parameter in the append request.
so like this:

        final RequestBody appendRq = RequestBody.create(MediaType.parse("multipart/form-data"), "APPEND");
        final RequestBody mediaId = RequestBody.create(MediaType.parse("multipart/form-data"), mediaIdStr);
        final RequestBody bytes = RequestBody.create(MediaType.parse("multipart/form-data"), String.valueOf(chunkPart));

and then used those in my call, (MediaAppend is an empty class i’ve made to avoid stackoverflow)
Call<RetroServiceCreator.MediaAppend> append = uploadService.appendMedia(appendRq, mediaId, media, bytes);

I assume you’ve fixed the problem already but to those that might stumble upon this thread, hoping to find a solution, you’re welcome :slight_smile:


#4

successfully got success response from INIT,APPEND and FIANLIZE…
after that when i use the media id with Statuses/update to tweet video over twitter by using below code this method i call in success response of media/upload(FINALIZE)…

private void postVideoTweet(String text, long imageId) {
    StatusesService statusesService = TwitterCore.getInstance().getApiClient().getStatusesService();
    Call<Tweet> tweetCall = statusesService.update(text,
 null, 
false, 
null,
 null,
 null, 
false,
 false, String.valueOf(imageId));

    tweetCall.enqueue(new Callback<Tweet>() {
        @Override
        public void success(Result<Tweet> result) {
            Log.d("result", result.data.idStr);
            postTwitter(result.data.idStr);

        }

        @Override
        public void failure(TwitterException exception) {
            hideProgressDialogResult();
            exception.printStackTrace();

        }
    });
}

i got expection…
com.twitter.sdk.android.core.TwitterApiException: HTTP request failed, Status: 400


#5

Plz provide some details about this,
i m not getting any response from twitter staff anywhere…the same detail i used to post but no one is giving the appropriate answer…
https://twittercommunity.com/t/media-uploads-init-appned-finalize-succuss-but-video-not-showing-on-twitter/101347

plz provide some feedback over this