iup_instance = Infinite_Uploads::get_instance(); $this->api = Infinite_Uploads_Api_Handler::get_instance(); /* //for testing, override values that our API would normally provide TODO remove add_filter( 'infinite_uploads_video_config', function ( $return, $key, $data ) { if ( 'library_id' === $key ) { return 56793; } elseif ( 'url' === $key ) { return 'https://vz-30d13541-113.b-cdn.net'; } elseif ( 'key_read' === $key ) { return BUNNY_API_KEY; } elseif ( 'key_write' === $key ) { return BUNNY_API_KEY; } elseif ( 'enabled' === $key ) { return defined( 'BUNNY_API_KEY' ); } return $return; }, 10, 3 ); */ add_action( 'wp_ajax_infinite-uploads-video-activate', [ &$this, 'ajax_activate_video' ] ); if ( $this->is_video_active() ) { add_action( 'admin_menu', [ &$this, 'admin_menu' ], 20 ); add_action( 'network_admin_menu', [ &$this, 'admin_menu' ], 20 ); //all write API calls we make on backend to not expose write API key add_action( 'wp_ajax_infinite-uploads-video-create', [ &$this, 'ajax_create_video' ] ); add_action( 'wp_ajax_infinite-uploads-video-update', [ &$this, 'ajax_update_video' ] ); add_action( 'wp_ajax_infinite-uploads-video-delete', [ &$this, 'ajax_delete_video' ] ); add_action( 'wp_ajax_infinite-uploads-video-settings', [ &$this, 'ajax_update_settings' ] ); //gutenberg block add_action( 'init', [ &$this, 'register_block' ] ); add_action( 'enqueue_block_editor_assets', [ &$this, 'script_enqueue' ] ); } //shortcode add_shortcode( 'infinite-uploads-vid', [ &$this, 'shortcode' ] ); } /** * * @return Infinite_Uploads_Video */ public static function get_instance() { if ( ! self::$instance ) { self::$instance = new Infinite_Uploads_Video(); } return self::$instance; } /* * Check if Video is created/active for this site. * * @return bool */ public function is_video_active() { return (bool) $this->get_config( 'library_id' ); } /* * Check if Video uploading/viewing is enabled. (When user has billing issues, * we will disable embeds and return only the read_key for viewing the library but no editing) * * @return bool */ public function is_video_enabled() { return $this->is_video_active() && $this->get_config( 'enabled' ); } /** * Activates Video service for this site. * * @return object|false */ public function activate_video() { $result = $this->api->call( "site/" . $this->api->get_site_id() . "/video", [], 'POST' ); if ( $result ) { //cache the new creds/settings once we enable video return $this->get_library_settings( true ); } return false; } /** * Update the video library settings. * * @return object|false */ public function update_library_settings( $args = [] ) { $new_settings = $this->api->call( "site/" . $this->api->get_site_id() . "/video", $args, 'POST' ); if ( $new_settings ) { //TODO don't make another api call, just update the cache return $this->get_library_settings( true ); } return $new_settings; } /** * Get the video library settings. They are cached 12hrs in the options table from the regular get_site_data call by default. * * @param bool $force_refresh Force a refresh of the settings from api. * * @return object|null */ public function get_library_settings( $force_refresh = false ) { $data = $this->api->get_site_data( $force_refresh ); if ( isset( $data->video->settings ) ) { return $data->video->settings; } return null; } /** * Get the video configuration value for a given key. They are cached 12hrs in the options table from the regular get_site_data call by default. * * @param string $key The key to get (enabled, library_id, key_write, key_read, url). * @param bool $force_refresh Force a refresh of the credentials. * * @return mixed */ public function get_config( $key, $force_refresh = false ) { $data = $this->api->get_site_data( $force_refresh ); if ( $override = apply_filters( 'infinite_uploads_video_config', null, $key, $data ) ) { return $override; } if ( isset( $data->video->{$key} ) ) { return $data->video->{$key}; } else { return null; } } /** * Perform an API request to Bunny Video API. * * @param string $path API path. * @param array $data Data array. * @param string $method Method. Default: POST. * * @return object|WP_Error */ private function api_call( $path, $data = [], $method = 'POST' ) { $library_id = $this->get_config( 'library_id' ); $url = "https://video.bunnycdn.com/library/{$library_id}/" . ltrim( $path, '/' ); $headers = array( 'Accept' => 'application/json', 'AccessKey' => $this->get_config( 'key_write' ), 'Content-Type' => 'application/json', ); $args = array( 'headers' => $headers, 'sslverify' => true, 'method' => strtoupper( $method ), 'timeout' => 30, ); switch ( strtolower( $method ) ) { case 'post': $args['body'] = wp_json_encode( $data ); $response = wp_remote_post( $url, $args ); break; case 'post_file': $args['body'] = $data; $args['headers']['Content-Type'] = 'application/binary'; $args['method'] = 'POST'; $response = wp_remote_post( $url, $args ); break; case 'get': if ( ! empty( $data ) ) { $url = add_query_arg( $data, $url ); } $response = wp_remote_get( $url, $args ); break; default: if ( ! empty( $data ) ) { $args['body'] = wp_json_encode( $data ); } $response = wp_remote_request( $url, $args ); break; } if ( is_wp_error( $response ) ) { return $response; } $body = json_decode( wp_remote_retrieve_body( $response ) ); if ( ! in_array( wp_remote_retrieve_response_code( $response ), [ 200, 201, 202, 204, 204 ], true ) ) { error_log( "Bunny API error: " . wp_remote_retrieve_response_code( $response ) . " " . wp_remote_retrieve_body( $response ) ); if ( isset( $body->ErrorKey ) ) { return new WP_Error( $body->ErrorKey, $body->Message, [ 'status' => wp_remote_retrieve_response_code( $response ) ] ); } else { return new WP_Error( 'bunny_api_error', wp_remote_retrieve_response_message( $response ), [ 'status' => wp_remote_retrieve_response_code( $response ) ] ); } } return $body; } /** * Enqueue the block's assets for the editor. * * @see https://developer.wordpress.org/block-editor/tutorials/block-tutorial/applying-styles-with-stylesheets/ */ function script_enqueue() { $data = array( 'libraryId' => $this->get_config( 'library_id' ), 'cdnUrl' => 'https://' . $this->get_config( 'url' ), // This give us the base CDN url for the library for building media links. 'apiKey' => $this->get_config( 'key_write' ), //we only expose the read key to the frontend. The write key is only used via backend ajax wrappers. 'settings' => $this->get_library_settings(), 'nonce' => wp_create_nonce( 'iup_video' ), //used to verify the request is coming from the frontend, CSRF. 'assetBase' => plugins_url( 'assets', __FILE__ ), 'settingsUrl' => $this->settings_url(), 'libraryUrl' => $this->library_url(), ); wp_register_script( 'iup-dummy-js-header', '' ); wp_enqueue_script( 'iup-dummy-js-header' ); wp_add_inline_script( 'iup-dummy-js-header', 'const IUP_VIDEO = ' . json_encode( $data ) . ';' ); } /** * Check permissions for a ajax request. * * @param $nonce * * @return void */ public function ajax_check_permissions( $nonce = 'iup_video' ) { // check caps if ( ! current_user_can( 'upload_files' ) ) { wp_send_json_error( esc_html__( 'Permissions Error: Please refresh the page and try again.', 'infinite-uploads' ) ); } //check nonce if ( ! check_ajax_referer( $nonce, 'nonce', false ) ) { wp_send_json_error( esc_html__( 'Permissions Error: Please refresh the page and try again.', 'infinite-uploads' ) ); } // return error if video is not enabled if ( $this->is_video_active() && ! $this->is_video_enabled() ) { wp_send_json_error( esc_html__( 'Infinite Uploads Video is disabled due to an issue with your account.', 'infinite-uploads' ) ); } } /** * Create a video in the video library, and returns the params for executing the tus upload. * * @see https://docs.bunny.net/reference/video_createvideo * @see https://docs.bunny.net/reference/tus-resumable-uploads * * @return void */ public function ajax_create_video() { $this->ajax_check_permissions(); $result = $this->api_call( '/videos', [ 'title' => sanitize_text_field( wp_unslash( $_REQUEST['title'] ) ) ] ); if ( is_wp_error( $result ) ) { wp_send_json_error( $result ); } //generate the signature params for a tus upload. $expiration = time() + ( 6 * HOUR_IN_SECONDS ); $response = [ 'AuthorizationSignature' => hash( 'sha256', $result->videoLibraryId . $this->get_config( 'key_write' ) . $expiration . $result->guid ), // SHA256 signature (library_id + api_key + expiration_time + video_id) 'AuthorizationExpire' => $expiration, // Expiration time as in the signature, 'VideoId' => $result->guid, // The guid of a previously created video object through the Create Video API call 'LibraryId' => $result->videoLibraryId, ]; wp_send_json_success( $response ); } /** * Update a video in the video library. * * @see https://docs.bunny.net/reference/video_updatevideo * * @return void */ public function ajax_update_video() { $this->ajax_check_permissions(); $video_id = sanitize_text_field( $_REQUEST['video_id'] ); if ( isset( $_REQUEST['title'] ) ) { $title = sanitize_text_field( wp_unslash( $_REQUEST['title'] ) ); $result = $this->api_call( "/videos/$video_id", compact( 'title' ) ); } elseif ( isset( $_REQUEST['thumbnail'] ) ) { $thumbnail = sanitize_text_field( $_REQUEST['thumbnail'] ); $result = $this->api_call( "/videos/$video_id/thumbnail?thumbnailUrl=" . $thumbnail ); } elseif ( isset( $_FILES['thumbnailFile'] ) ) { $result = $this->api_call( "/videos/$video_id/thumbnail", file_get_contents( $_FILES['thumbnailFile']['tmp_name'] ), 'POST_FILE' ); } else { wp_send_json_error( esc_html__( 'Invalid request', 'infinite-uploads' ) ); } if ( is_wp_error( $result ) ) { wp_send_json_error( $result ); } wp_send_json_success( $result ); } /** * Delete a video in the video library. * * @see https://docs.bunny.net/reference/video_deletevideo * * @return void */ public function ajax_delete_video() { $this->ajax_check_permissions(); $video_id = sanitize_text_field( $_REQUEST['video_id'] ); $result = $this->api_call( "/videos/$video_id", [], 'DELETE' ); if ( is_wp_error( $result ) ) { wp_send_json_error( $result ); } wp_send_json_success( $result ); } /** * Update video library settings via Infinite Uploads API. * * @see https://docs.bunny.net/reference/video_updatevideo * * @return void */ public function ajax_update_settings() { $this->ajax_check_permissions(); //this is proxied to the Infinite Uploads API, and is sanitized/validated there. $settings = json_decode( wp_unslash( $_REQUEST['settings'] ) ); $result = $this->update_library_settings( $settings ); if ( ! $result ) { wp_send_json_error( $result ); } wp_send_json_success( $result ); } /** * Update video library settings via Infinite Uploads API. * * @see https://docs.bunny.net/reference/video_updatevideo * * @return void */ public function ajax_activate_video() { $this->ajax_check_permissions(); $result = $this->activate_video(); if ( ! $result ) { wp_send_json_error( $result ); } wp_send_json_success( $result ); } /** * Registers the video library page under Media. */ function admin_menu() { $page = add_media_page( __( 'Video Library - Infinite Uploads', 'infinite-uploads' ), __( 'Video Library', 'infinite-uploads' ), 'upload_files', 'infinite_uploads_vids', [ $this, 'library_page', ], 1.1678 //for unique menu position above Add New. ); add_action( 'admin_print_scripts-' . $page, [ &$this, 'script_enqueue' ] ); add_action( 'admin_print_scripts-' . $page, [ &$this, 'admin_scripts' ] ); add_action( 'admin_print_styles-' . $page, [ &$this, 'admin_styles' ] ); //video settings page. $page = add_submenu_page( 'infinite_uploads', __( 'Infinite Uploads Video', 'infinite-uploads' ), __( 'Video Cloud', 'infinite-uploads' ), $this->iup_instance->capability, 'infinite_uploads_video_settings', [ $this, 'settings_page', ] ); add_action( 'admin_print_scripts-' . $page, [ &$this, 'script_enqueue' ] ); add_action( 'admin_print_scripts-' . $page, [ &$this, 'admin_scripts' ] ); add_action( 'admin_print_styles-' . $page, [ &$this, 'admin_styles' ] ); } /** * Get the settings url with optional url args. * * @param array $args Optional. Same as for add_query_arg() * * @return string Unescaped url to settings page. */ function settings_url( $args = [] ) { if ( is_multisite() ) { $base = network_admin_url( 'admin.php?page=infinite_uploads_video_settings' ); } else { $base = admin_url( 'admin.php?page=infinite_uploads_video_settings' ); } return add_query_arg( $args, $base ); } /** * Get the settings url with optional url args. * * @param array $args Optional. Same as for add_query_arg() * * @return string Unescaped url to settings page. */ function library_url( $args = [] ) { $base = admin_url( 'upload.php?page=infinite_uploads_vids' ); return add_query_arg( $args, $base ); } function register_block() { register_block_type( __DIR__ . '/video/block' ); } /** * @todo adjust for the video library page. */ function admin_scripts() { wp_enqueue_script( 'iup-admin', plugins_url( 'build/admin.js', __DIR__ ), array( 'wp-element', 'wp-i18n' ), ( wp_get_environment_type() !== 'production' ? time() : INFINITE_UPLOADS_VERSION ), false ); } /** * */ function admin_styles() { wp_enqueue_style( 'iup-uppy', plugins_url( 'build/style-block.css', __DIR__ ), false, INFINITE_UPLOADS_VERSION ); //Have no idea why webpack is putting uppy css in this file. wp_enqueue_style( 'iup-admin', plugins_url( 'build/admin.css', __DIR__ ), false, INFINITE_UPLOADS_VERSION ); } /** * Video library page display callback. */ function library_page() { ?>
is_video_active() ) { return ''; } $atts = shortcode_atts( [ 'id' => '', 'autoplay' => 'false', 'loop' => 'false', 'muted' => 'false', 'preload' => 'true', ], $atts, 'infinite-uploads-vid' ); // Force preload to always be "true" $atts['preload'] = 'true'; $video_url = esc_url_raw( sprintf( 'https://iframe.mediadelivery.net/embed/%d/%s', $this->get_config( 'library_id' ), $atts['id'] ) ); unset( $atts['id'] ); // Ensure autoplay is always set to "false" if not explicitly defined if ( ! isset( $atts['autoplay'] ) ) { $atts['autoplay'] = 'false'; } $video_url = add_query_arg( $atts, $video_url ); //fully escape now $video_url = esc_url( $video_url ); return <<
HTML; } /** * Video library page display callback. * * @todo This should be adapted to all bootstrap-react in it's own template files loaded by admin_scripts(). */ function video_library_page2() { ?>

Infinite Uploads Logo

site ) && ! $api_data->site->cdn_enabled ) { ?> site ) && ! $api_data->site->upload_writeable ) { ?>