tusプロトコルを使ってPHPで vimeo に動画をアップロードする

tusを使ったvimeoに動画アップロードを実装しました。WordPerssベースのシステムでの機能アップデートのため、PHP+JSでの開発になります。

    経緯

    vimeo から「従来のストリーミングアップロード方式が廃止となるため新しい実装方式に移行してください」とのアナウンスがあったとのことで、対応したいという案件をです。vimeo からのメールはこちら。

    We’ve noticed **** is using the deprecated streaming upload approach. We’re writing to inform you that we’ll be sunsetting the streaming upload approach on January 31, 2023. We strongly encourage you to upgrade to the improved resumable/tus upload approach. For more information about the tus protocol at Vimeo, see our Working with Video Uploads guide.

    If you’re currently using an Offical SDK, upgrade to the latest version to utilize the tus upload protocol.

    Need help? Click here to contact our Support team and we will get back to you as soon as possible.

    Best,
    Vimeo Support

    ドキュメントを見ながらやっていきます。

    動画のアップロード方法として以下の3つのアプローチが案内されます。

    • Resumable, or tus, after the open-source tus protocol
    • Form-based, also known as POST
    • Pull

    tus を使うのが進捗状況の表示などもできていちばんオススメだが、工数的にはフォームで POST するのがラクとのこと。Pull はどこか別のサーバー上に動画がアップロードされている場合に使う。今回はメールでもオススメされているし、既存のUIで実装されている進捗表示のプログレスバーを引き続き生かしたいので、tus を採用することに。

    そもそも tus 自体はじめて聞いたが、ファイルアップロードのためのプロトコルとのこと。アップロードが中断した時に、途中で失敗したところから再開できたり、進捗を表示することもできるなど。

    実装準備

    vimeo 上でアプリを作る

    My Appsからアプリを作成し、Client identifieraccess tokenClient secretsを取得する。access tokenの生成時に「Authenticated (you)」を選んで「Private」にチェックを入れて、合わせて必要な権限にもチェックを付けておく必要あり。 Getting Started with the Vimeo API

    ライブラリのインストール

    comporser 経由でインストール。

    composer require vimeo/vimeo-api

    Getting Started with the Vimeo API

    GitHubもある。 vimeo/vimeo.php: Official PHP library for the Vimeo API.

    その他の言語のライブラリとかはこちら。 Vimeo API Libraries and SDKs

    動画アップロード処理の実装

    フロントエンド

    JavaScript
    document.addEventListener('DOMContentLoaded', () => {
    	const file_select = document.getElementById('file_input');
    	if( file_input ){
    		file_input.addEventListener('click', (evt) => {
    			evt.target.value = ''; // エラー等で再選択する時に同じファイルでもchangeイベントを発火させるための処理
    		});
    		file_input.addEventListener('change', () => {
    			const file = evt.target.files[0];
    			vimeo_upload(file);
    		}, false);
    	}
    });
    
    const vimeo_upload = (file) => {
    	let filedata = new FormData();
    	filedata.append('file', file);
    	filedata.append('action', 'vimeo_tus_upload');
    	filedata.append('nonce', nonce);
    	filedata.append('title', get_video_title());
    
    	jQuery.ajax({
    		type: 'POST',
    		dataType: 'JSON',
    		url: ajaxurl,
    		processData: false,
    		contentType: false,
    		cache: false,
    		data: filedata
    	}).done(function( ret ){
    		if( $ret[0] =- 'success' ){
    			// 成功時の処理
    		}else{
    			// エラー時の処理
    		}
    	}).fail(function( xhr, status, errorThrown ){
    		// 通信失敗時の処理
    	});
    }

    actionとかnonceとかやってるのはWordPress仕様。

    PHP でアップロード

    ライブラリに用意されたuploadメソッドでアップロードするパターン。サーバーサイドで完結するので、プログレスバーの実装はできない(はず)。

    vimeo のドキュメントより、ライブラリのコードを直接読むか、ライブラリ内のexampleを読んだ方が分かりやすいかも。

    PHP
    require '{path_to_root_folder}/autoload.php';
    use Vimeo\Vimeo;
    use Vimeo\Exceptions\VimeoUploadException;
    
    function get_vimeo_client(){
    	$client = new Vimeo("{client_id}", "{client_secret}", "{access_token}");
    }
    
    function vimeo_tus_upload(){
    	self::check_nonce( $_POST['nonce'] );// 省略
    	$client = self::get_vimeo_client();
    
    	$file = $_FILES['file']['tmp_name'];
    
    	if( empty($file) ){
    		$response = array( 'error', 'ファイルを読み込めませんでした' );
    		echo json_encode( $response );
    		die();
    	}
    
    	try{
    		$url = $client->upload(
    			$file,
    			array(
    				'name' => 'Vimeo API SDK test upload',
    				'description' => "This video was uploaded through the Vimeo API's PHP SDK.",
    			)
    		);
    	}catch( VimeoUploadException $e ){
    		$response = array( 'error', $e->getMessage() );
    		echo json_encode( $response );
    		die();
    	}
    
    	$url = end( explode( '/', $url ) );
    	$response = array( 'success', $url );
    	echo json_encode($response);
    
    	die();
    }

    なお、レスポンスはvideo/video_idの形式で返ってくるが、https://vimeo.com/video/{video_id}にアクセスしても404になってhttps://vimeo.com/{video_id}だとアクセスできる。このへんのvimeoの仕様はもうちょっと深堀りした方がいいのかもしれない。

    JavaScript でアップロード

    サーバーサイドではアップロード先のURLだけ取得して、JSでアップロード処理を行う。プログレスバーが欲しい時はこれ。

    tus クライアントをインストール

    $ npm install --save tus-js-client

    またはtus.min.jsをダウンロードして読み込む。

    <script src="./tus.min.js"></script>

    アップロード先のURLを取得(PHP)

    PHP
    function vimeo_upload_request(){
    	self::check_nonce( $_POST['nonce'] );
    	$client = self::get_vimeo_client();
    
    	try{
    		$params['upload']['approach'] = 'tus';
    		$params['upload']['size'] = $_POST['size'];
    		// 任意
    		$params['name'] = 'Vimeo API SDK test upload';
    		$params['description'] = "This video was uploaded through the Vimeo API's PHP SDK.";
    
    		$uri = '/me/videos?fields=uri,upload';
    
    		$attempt = $client->request($uri, $params, 'POST');
    		if ($attempt['status'] == 200) {
    			$url = end( explode( '/', $attempt['body']['uri'] ) );
    			$response = array( 'success', $url, $attempt['body']['upload']['upload_link'] );
    			echo json_encode($response);
    		}else{
    			$attempt_error = !empty($attempt['body']['error']) ? ' [' . $attempt['body']['error'] . ']' : '';
    			$response = array( 'error', $attempt_error );
    			echo json_encode($response);
    		}
    	}catch( VimeoUploadException $e ){
    		$response = array( 'error', $e->getMessage() );
    		echo json_encode($response);
    	}
    	die();
    	}

    アップロード

    vimeo のドキュメントには書いてないので JS クライアントのドキュメントを見る。「Example: Upload to Vimeo」というドンピシャの例があるので助かる。

    JavaScript
    const vimeo_upload = (file) => {
    	jQuery.ajax({
    		type: 'POST',
    		dataType: "JSON",
    		url: vimeo_ajax_object.ajaxurl,
    		data: {
    			'action': 'vimeo_upload_request',
    			'nonce' : vimeo_ajax_object.ajaxnonce,
    			'size'  : file.size
    		}
    	}).done(function(ret){
    		if( ret[0] == 'success' ){
    			const url = ret[1];
    			const upload_url = ret[2];
    			vimeo_upload_tus( upload_url, url, file );
    		}else{
    			// リクエスト失敗時の処理
    		}
    	}).fail(function( xhr, status, errorThrown ){
    		// 通信失敗時の処理
    	});
    }
    
    const vimeo_upload_tus = ( upload_url, vimeo_id, file ) => {
    	const upload = new tus.Upload(file, {
    		uploadUrl: upload_url,
    		onError: function(error) {
    			// 失敗時の処理
    		},
    		onProgress: function( bytesUploaded, bytesTotal ) {
    			const percentage = (bytesUploaded / bytesTotal * 100).toFixed(0);
    			let progress = document.getElementById('progress');
    			progress.setAttribute('style', 'width:' + percentage + '%');
    			progress.innerHTML = ' ' + percentage + '%';
    		},
    		onSuccess: function() {
    			// 成功時の処理
    		}
    	});
    	upload.start();
    }

    privacy の設定

    アップロードのリクエスト時、 metadata に privacy 設定を追加することができる。この際、アカウントがフリープランだと privacy 設定に対応していないためエラーになる。

    This feature requires a paid plan on Vimeo.

    Manage your video’s privacy settings – Help Center

    エラー例

    (
    	[invalid_parameters] => Array
    		(
    			[0] => Array
    				(
    					[field] => privacy.download
    					[error_code] => 2204
    					[error] => You have provided an invalid parameter. Please contact developer of this application.
    					[developer_message] => The parameters passed to this API endpoint didn't pass Vimeo's validation. Please check the invalid_parameters list for more information.
    				)
    
    		)
    
    	[error] => You have provided an invalid parameter. Please contact developer of this application.
    	[link] => 
    	[developer_message] => The parameters passed to this API endpoint didn't pass Vimeo's validation. Please check the invalid_parameters list for more information.
    	[error_code] => 2204
    )

    ユーザーのプラン情報を取得

    ユーザーによってプランが混在するような環境ではこれを回避するため、ユーザー情報からプランを確認する。

    PHP
    try{
    	$uri ='/me';
    	$user = $client->request($uri, '', 'GET');
    	$account = $user['body']['account'];
    }catch( VimeoRequestException $e ){
    	echo json_encode($e->getMessage());
    	die();
    }
    $params = array(
    	'upload' => array(
    		'approach' => 'tus',
    		'size'	=> $_POST['size'],
    	),
    	'name' => $_POST['title'],
    );
    if( $account != 'free' ){
    	$params['privacy']['download'] = false;
    }

    埋め込みプリセットの設定

    ビューアーのデザインを設定し、プリセット( presets )として設定することができる。プリセットは以下より作成できる。

    プリセット設定画面URL末尾の数字がpreset_idになる。

    以下はアップロードした動画に特定のプリセットを設定する例。

    PHP
    try{
    	$uri ='/videos/' . video_id . '/presets/' . preset_id;
    	$response = $client->request($uri, '', 'PUT');
    	if( !empty($response['body']['error']) ){
    		// エラー処理
    	}
    }catch( VimeoRequestException $e ){
    	// エラー処理
    	die();
    }

    なお、デフォルトのプリセットは 設定 > 動画 > アップロードのデフォルト > 動画に適用する埋め込みプリセットを選択。 で設定可能だが、プリセットを作成したあと一度リロードしないと選択肢に表示されない。

    その他、各種処理は以下。

    削除処理の実装

    フロント省略。

    PHP
    function vimeo_delete(){
    	self::check_nonce( $_POST['nonce'] );
    	$client = self::get_vimeo_client();
    	$vimeo_id = $_POST['vimeo_id']; // 動画IDを指定する
    	try {
    		$uri = '/videos/' . $vimeo_id;
    		$response = $client->request($uri, [], 'DELETE');
    	} catch ( VimeoUploadException $e ) {
    		$response = array( 'error', $e->getMessage() );
    		echo json_encode($response);
    		die();
    	}
    	$response = array( 'success', $response['status'] );
    	echo json_encode($response);
    	die();
    }

    その他参考

    アップロード時の各種パラメーターは以下を参照。

    他の言語の記事もあって参考になった。感謝。

    サポートが必要ですか?

    ご質問・お見積り依頼はお気軽にどうぞ

    お問い合わせはこちら
    シェア
    野良人 代表
    新免祥太
    1988年岡山生まれ。外食企業のWEB・EC担当を経験したのち、2013年12月より「野良人(のらんど)」の屋号で独立しWEBデザイン・プログラミングなどWEBサイト制作の工程全般を請け負っています。お気軽にご相談ください。
    広告
    前の記事(2022/12/28)
    WordPress の wp_insert_post() で投稿した記事が WP_Query や get_posts() で取得できない時の確認ポイント
    記事一覧