45
38

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-06-22

概要

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";
});

45
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
38