oont-contents/plugins/jetpack/jetpack_vendor/automattic/jetpack-videopress/src/class-uploader.php
2025-02-08 15:10:23 +01:00

300 lines
7.8 KiB
PHP

<?php
/**
* VideoPress Uploader
*
* @package automattic/jetpack-videopress
*/
namespace Automattic\Jetpack\VideoPress;
use Jetpack_Options;
use VideoPressUploader\File_Exception;
use VideoPressUploader\Tus_Client;
/**
* VideoPress Uploader class
*
* Handles the upload from the Media Library to VideoPress servers
*/
class Uploader {
/**
* The key of the post meta that holds the ID of the attachment that holds the VideoPress video, in case this attachment was uploaded before.
*
* @var string
*/
const UPLOADED_KEY = '_videopress_uploaded_id';
/**
* The chunk size of each upload step
*
* @var int
*/
const CHUNK_SIZE = 5000000;
/**
* The Tus Client instance
*
* @var Tus_Client
*/
protected $client = null;
/**
* The attachment ID
*
* @var int
*/
protected $attachment_id;
/**
* Checks whether this feature is supported by the server
*
* @param int $attachment_id The ID of the video attachment we want to upload to VideoPress.
* @return boolean
*/
public static function is_valid_attachment_id( $attachment_id ) {
$file_path = get_attached_file( $attachment_id );
if ( ! $file_path || ! is_readable( $file_path ) ) {
return false;
}
if ( ! str_starts_with( get_post_mime_type( $attachment_id ), 'video/' ) ) {
return false;
}
return true;
}
/**
* Constructs the object
*
* @throws Upload_Exception If attachment is invalid or server does not support it.
* @param int $attachment_id The ID of the video attachment we want to upload to VideoPress.
*/
public function __construct( $attachment_id ) {
$this->attachment_id = $attachment_id;
if ( ! $this->get_file_path() ) {
throw new Upload_Exception( __( 'Invalid attachment ID', 'jetpack-videopress-pkg' ), Upload_Exception::ERROR_INVALID_ATTACHMENT_ID );
}
if ( ! is_readable( $this->get_file_path() ) ) {
throw new Upload_Exception( __( 'File not found', 'jetpack-videopress-pkg' ), Upload_Exception::ERROR_FILE_NOT_FOUND );
}
if ( ! $this->file_has_supported_mime_type() ) {
throw new Upload_Exception( __( 'Mime type not supported', 'jetpack-videopress-pkg' ), Upload_Exception::ERROR_MIME_TYPE_NOT_SUPPORTED );
}
}
/**
* Gets the path of the video file
*
* @return string
*/
public function get_file_path() {
return get_attached_file( $this->attachment_id );
}
/**
* Gets the mime type of the attachment
*
* @return string
*/
public function get_file_mime_type() {
return get_post_mime_type( $this->attachment_id );
}
/**
* Gets the name of the video file
*
* @return string
*/
public function get_file_name() {
return basename( $this->get_file_path() );
}
/**
* Gets the size of the video file
*
* @return int
*/
public function get_file_size() {
return filesize( $this->get_file_path() );
}
/**
* Checks if the mime type of the attachment is supported to be uploaded
*
* @return boolean
*/
public function file_has_supported_mime_type() {
return str_starts_with( $this->get_file_mime_type(), 'video/' );
}
/**
* Gets the VideoPress upload token
*
* @throws Upload_Exception If it fails to fetch the token.
*
* @return string
*/
public function get_upload_token() {
return VideoPressToken::videopress_upload_jwt();
}
/**
* Gets a unique upload key for this attachment
*
* @return string
*/
public function get_key() {
return sprintf( 's-%d-v-%d', Jetpack_Options::get_option( 'id' ), $this->attachment_id );
}
/**
* Sets the current attachment as uploaded and stores the ID of the VideoPress video attachment ID
*
* @param int $new_attachment_id The ID of the new attachment created to hold the VideoPress video.
* @return void
*/
protected function mark_as_uploaded( $new_attachment_id ) {
update_post_meta( $this->attachment_id, self::UPLOADED_KEY, $new_attachment_id );
}
/**
* Sets the current attachment as not being uploaded before. Deletes the reference to the videopress attachment
*
* @return void
*/
protected function unmark_as_uploaded() {
delete_post_meta( $this->attachment_id, self::UPLOADED_KEY );
}
/**
* Checks whether this attachment was uploaded before
*
* @return boolean
*/
public function is_uploaded() {
return ! empty( $this->get_uploaded_attachment_id() );
}
/**
* Gets the ID of the VideoPress video attachment in case this attachment was uploaded before
*
* @return boolean|string False if value is absent. Post ID on success.
*/
public function get_uploaded_attachment_id() {
return get_post_meta( $this->attachment_id, self::UPLOADED_KEY, true );
}
/**
* Retrieves the instance of the Tus_Client
*
* @return Tus_Client
*/
public function get_client() {
if ( $this->client !== null ) {
return $this->client;
}
$this->client = new Tus_Client( $this->get_key(), $this->get_upload_token(), Jetpack_Options::get_option( 'id' ) );
return $this->client;
}
/**
* Uploads a chunk of the file
*
* @return array With the status of the upload
*/
public function upload() {
if ( $this->is_uploaded() ) {
return $this->check_status();
}
try {
$this->get_client()->file( $this->get_file_path(), $this->get_file_name() );
$bytes_uploaded = $this->get_client()->upload( self::CHUNK_SIZE );
if ( $bytes_uploaded === $this->get_file_size() ) {
$this->mark_as_uploaded( $this->get_client()->get_uploaded_video_details()['media_id'] );
return array(
'status' => 'complete',
'bytes_uploaded' => $bytes_uploaded,
'file_size' => $this->get_file_size(),
'file_name' => $this->get_file_name(),
'upload_key' => $this->get_key(),
'uploaded_details' => $this->get_client()->get_uploaded_video_details(),
);
}
return array(
'status' => 'uploading',
'bytes_uploaded' => $bytes_uploaded,
'file_size' => $this->get_file_size(),
'file_name' => $this->get_file_name(),
'upload_key' => $this->get_key(),
);
} catch ( \Exception $e ) {
return array(
'status' => 'error',
'bytes_uploaded' => -1,
'file_size' => $this->get_file_size(),
'file_name' => $this->get_file_name(),
'upload_key' => $this->get_key(),
'error' => $e->getCode() . ': ' . $e->getMessage(),
);
}
}
/**
* Checks the status of the upload of this attachment
*
* @return array
*/
public function check_status() {
if ( $this->is_uploaded() ) {
$uploaded_attachment_id = $this->get_uploaded_attachment_id();
$uploaded_video_guid = get_post_meta( $uploaded_attachment_id, 'videopress_guid', true );
if ( $uploaded_video_guid ) {
return array(
'status' => 'uploaded',
'upload_key' => $this->get_key(),
'uploaded_post_id' => $uploaded_attachment_id,
'uploaded_video_guid' => $uploaded_video_guid,
);
} else {
// VideoPress attachment is gone, allow user to upload it again.
$this->unmark_as_uploaded();
}
}
try {
$offset = $this->get_client()->get_offset();
$status = false !== $offset ? 'resume' : 'new';
$offset = false === $offset ? 0 : $offset;
return array(
'status' => $status,
'bytes_uploaded' => $offset,
'file_size' => $this->get_file_size(),
'file_name' => $this->get_file_name(),
'upload_key' => $this->get_key(),
);
} catch ( File_Exception $e ) {
return array(
'status' => 'resume',
'bytes_uploaded' => 0,
'file_size' => $this->get_file_size(),
'file_name' => $this->get_file_name(),
'upload_key' => $this->get_key(),
);
} catch ( \Exception $e ) {
return array(
'status' => 'error',
'bytes_uploaded' => -1,
'file_size' => $this->get_file_size(),
'file_name' => $this->get_file_name(),
'message' => $e->getMessage(),
);
}
}
}