Help us understand the problem. What is going on with this article?

TwitterAPIのアップロード系エンドポイントまとめ (140秒動画対応)

More than 3 years have passed since last update.

概要

Twitterが140秒までの動画ツイートに対応したと聞いたので,以前からあるエンドポイントも含めてアップロード系のものをまとめてみました.

サンプルには拙作のTwistOAuthを用いたコードを掲載します.

POST statuses/update_with_media

単一の画像のアップロードに使えます.

echo "Uploading and tweeting...\n";
$to->postMultipart('statuses/update_with_media', [
    'status' => 'hello',
    '@media[]' => 'photo.jpg',
]);

echo "Done!\n";
  • 現在非推奨
  • サーバとの通信が1回で済む
  • multipart/form-dataが強制される
    • エンドポイント固有のパラメータはoauth_signatureの生成対象に含まれない
  • media[]というパラメータ名であるにも関わらず,単一の画像しか受け付けない

POST media/upload (コマンド無し)

1枚以上の画像のアップロードに使えます.

$media_ids = [];

echo "Uploading A.jpg...\n";
$media_ids[] = $to->postMultipart('media/upload', [
    '@media' => 'A.jpg',
])->media_id_string;

echo "Uploading B.jpg...\n";
$media_ids[] = $to->post('media/upload', [
    '@media' => 'B.jpg', // 自動的にデータがBase64エンコードされる
])->media_id_string;

echo "Tweeting...\n";
$to->post('statuses/update', [
    'status' => 'hello',
    'media_ids' => implode(',', $media_ids),
]);

echo "Done!\n";
  • サーバとの通信が2回以上必要
  • multipart/form-dataとapplication/x-www-form-urlencodedから選べる
    • multipart/form-dataの場合,エンドポイント固有のパラメータはoauth_signatureの生成対象に含まれない
    • application/x-www-form-urlencodedの場合,メディアデータのみBase64エンコードする必要がある

オーバーヘッドの小さいmultipart/form-dataを推奨します.

POST media/upload (コマンドあり・同期)

いろいろなデータのアップロードに使えます.

  • 1枚以上の画像
  • 1つのアニメGIF (0.5秒〜30秒)
  • 1つの動画 (0.5秒〜30秒)

以下のコマンドを順番にコールする必要があります.

  1. INIT
  2. APPEND
  3. FINALIZE
$filename = 'small_video.mp4';

echo "Initializing...\n";
$info = $to->post('media/upload', [
    'command' => 'INIT',
    'media_type' => 'video/mp4',
    'total_bytes' => filesize($filename),
]);

echo "Uploading video...\n";
$to->postMultipart('media/upload', [
    'command' => 'APPEND',
    'media_id' => $info->media_id_string,
    'segment_index' => 0,
    '@media' => $filename,
]);

echo "Finalizing...\n";
$to->post('media/upload', [
    'command' => 'FINALIZE',
    'media_id' => $info->media_id_string,
]);

echo "Tweeting...\n";
$to->post('statuses/update', [
    'status' => 'hello',
    'media_ids' => $info->media_id_string,
]);

echo "Done!\n";
  • サーバとの通信が4回以上必要
  • multipart/form-dataとapplication/x-www-form-urlencodedから選べる
    • multipart/form-dataの場合,エンドポイント固有のパラメータはoauth_signatureの生成対象に含まれない
    • application/x-www-form-urlencodedの場合,メディアデータのみBase64エンコードする必要がある

オーバーヘッドの小さいmultipart/form-dataを推奨します.

GET|POST media/upload (コマンドあり・非同期)

いろいろなデータのアップロードに使えます.こちらは大きなデータをアップロードする前提なので,チャンク分割を入れます.分割を行わないと,413 Request Entity Too Largeが返されてしまう場合があります.

  • 1枚以上の画像
    • media_category=tweet_image
  • 1つのアニメGIF (0.5秒〜140秒)
    • media_category=tweet_gif
  • 1つの動画 (0.5秒〜140秒)
    • media_category=tweet_video

以下のコマンドを順番にコールする必要があります.

  1. INIT (media_category指定必須)
  2. APPEND
  3. FINALIZE
  4. STATUS
$file = new \SplFileObject('large_video.mp4', 'rb');
$chunk_size = 262144; // 適当に調節してください

echo "Initializing...\n";
$info = $to->post('media/upload', [
    'command' => 'INIT',
    'media_type' => 'video/mp4',
    'total_bytes' => $file->getSize(),
    'media_category' => 'tweet_video', // これを忘れると非同期のほうにならない
]);

$curls = [];
for ($i = 0; '' !== $buffer = $file->fread($chunk_size); ++$i) { // チャンクサイズごとに分割
    $curls[] = $to->curlPostMultipart('media/upload', [
        'command' => 'APPEND',
        'media_id' => $info->media_id_string,
        'segment_index' => $i,
        '#media' => $buffer,
    ]); // リクエストせずにリクエスト用のcURLリソースだけを用意
}

echo "Uploading chunks...\n";
$to->curlMultiExec($curls, true); // 1チャンクごとに待ってると時間がかかるので並列化

echo "Finalizing...\n";
$info = $to->post('media/upload', [
    'command' => 'FINALIZE',
    'media_id' => $info->media_id_string,
]);

echo "Waiting for processing...\n";
while ($info->processing_info->state === 'pending' || $info->processing_info->state === 'in_progress') { // 終わるか失敗するまで繰り返す
    $percent = isset($info->processing_info->progress_percent) // 場合によってはパーセント表記が返ってくる
        ? "({$info->processing_info->progress_percent}%)"
        : ''
    ;
    echo "State: {$info->processing_info->state} {$percent}\n"; 
    sleep($info->processing_info->check_after_secs); // 待つように指示されたぶんだけ待つ
    $info = $to->get('media/upload', [
        'command' => 'STATUS',
        'media_id' => $info->media_id_string,
    ]); // 情報を更新
}

if ($info->processing_info->state === 'failed') {
    // 失敗時は例外をスロー
    throw new \RuntimeException($info->processing_info->error->name);
}

echo "Tweeting...\n";
$to->post('statuses/update', [
    'status' => 'hello',
    'media_ids' => $info->media_id_string,
]);

echo "Done!\n";
  • サーバとの通信がかなりの回数必要
  • multipart/form-dataとapplication/x-www-form-urlencodedから選べる
    • multipart/form-dataの場合,エンドポイント固有のパラメータはoauth_signatureの生成対象に含まれない
    • application/x-www-form-urlencodedの場合,メディアデータのみBase64エンコードする必要がある
  • 失敗した時に$info->processing->state"fail"になることがある
    • しかし,そのときのHTTPステータスコードは200 OK
    • エラー内容次第では従来の {"errors":[{"code":1,"message":"xx"}]}400 Bad Requestで来ることもある
    • 失敗したメディアでstatuses/updateしようとするとしっかり400 Bad Requestのエラーになるので,必ずしもこのエラーハンドリングを特別扱いして行う必要はない

オーバーヘッドの小さいmultipart/form-dataを推奨します.

追記: TwistOAuthの後継であるCowitterでは,面倒なアップロードのための記述を丸投げできます

Cowitterのメソッドを活用する場合
Co::wait(function () use ($client) {
    $file = new \SplFileObject('large_video.mp4', 'rb');

    $on_uploading = function ($percent) {
        echo "Uploading ... ({$percent}%)\n";
    };
    $on_processing = function ($percent) {
        echo "Processing ... ({$percent}%)\n";
    };

    yield $client->postAsync('statuses/update', [
        'status' => 'My video',
        'media_ids' => (yield $client->uploadVideoAsync($file, $on_uploading, $on_processing))->media_id_string,
    ]);

    echo "Done!\n";
});

mpyw
PHP(Laravel) / JavaScript(React/Redux/ReactNative/Vue) / MySQL あたりが得意分野なWeb系エンジニア。最近マンネリ化がひどいので Go / Kotlin / Rust / Swift あたりから何か掘り下げたいと思っている。Go は 2.x 出てから書きます。古い記事はそのまま参考にしないようにご注意ください
http://gravatar.com/mpyw
synapse
Synapseは、オンラインサロンサービスにおけるパイオニアとして、かつて存在していたスタートアップです。
https://synapseam.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした