setUsername($token_id) ->setPassword($token_secret); } catch (\Throwable $th) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- guarded by WP_DEBUG, intended for plugin debugging. error_log('Exception muxvideo_get_global_config'); } echo '
' . esc_html__('error_try_again', '2coders-integration-mux-video') . '
'; return ''; } } function muxvideo_get_request_headers() { $version = MUXVIDEO_PLUGIN_VERSION; return [ 'headers' => [ 'x-source-platform' => "2Coders Wordpress Free | {$version}" ] ]; } function muxvideo_getMuxConfig( $token_id, $token_secret ) { return MuxPhp\Configuration::getDefaultConfiguration() ->setUsername( $token_id ) ->setPassword( $token_secret ); } function muxvideo_get_signing_key_api() { $config = muxvideo_get_global_config(); return new MuxPhp\Api\URLSigningKeysApi( new GuzzleHttp\Client(), $config ); } // API Client Initialization function muxvideo_api_client_init() { $headers = muxvideo_get_request_headers(); $config = muxvideo_get_global_config(); return new MuxPhp\Api\AssetsApi( new GuzzleHttp\Client( $headers ), $config ); } // API Client Initialization to Uploads function muxvideo_api_client_init_upload() { $config = muxvideo_get_global_config(); return new MuxPhp\Api\DirectUploadsApi( new GuzzleHttp\Client(), $config ); } // API Client Initialization to playback restrictions function muxvideo_api_client_init_playback_restrictions() { $config = muxvideo_get_global_config(); try { return new MuxPhp\Api\PlaybackRestrictionsApi( new GuzzleHttp\Client(), $config ); } catch (\Throwable $th) { echo '
' . esc_html__('error_try_again', '2coders-integration-mux-video') . '
'; return ''; } } /** * Create and catch update options for private_key in SigningKeyResponse */ function mux_crypto_key(): string { return hash('sha256', SECURE_AUTH_KEY, true); } function mux_crypto_encrypt(string $plain): string { if ($plain === '') return ''; $key = mux_crypto_key(); $iv = random_bytes(16); $ct = openssl_encrypt($plain, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv); if ($ct === false) { throw new RuntimeException('Encrypt failed'); } return base64_encode($iv . $ct); } function mux_crypto_decrypt(string $b64) { if ($b64 === '') return ''; $raw = base64_decode($b64, true); if ($raw === false || strlen($raw) <= 16) return ''; $iv = substr($raw, 0, 16); $ct = substr($raw, 16); $key = mux_crypto_key(); $pt = openssl_decrypt($ct, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv); return $pt === false ? '' : $pt; } function mux_maybe_decrypt(string $value): string { if ($value === '') return ''; $dec = mux_crypto_decrypt($value); return $dec !== '' ? $dec : $value; } function muxvideo_add_field_to_options(array $new_options) { $option_name = 'muxvideo_options'; $token_id = isset($new_options['muxvideo_token_id']) ? trim($new_options['muxvideo_token_id']) : ''; $token_secret = $new_options['muxvideo_token_secret'] ?? ''; if ($token_id !== '' && !preg_match('/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i', $token_id)) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- guarded by WP_DEBUG, intended for plugin debugging. error_log('muxvideo: token_id no parece un UUID'); } if ($token_id && $token_secret) { $new_options['muxvideo_token_id'] = mux_crypto_encrypt($token_id); $new_options['muxvideo_token_secret'] = mux_crypto_encrypt($token_secret); $new_options['muxvideo_signing_data'] = muxvideo_create_signing_key(); update_option($option_name, $new_options, false); } } function muxvideo_handle_option_update( $option_name, $old_value, $new_value ) { if ( $option_name === 'muxvideo_options' ) { remove_action( 'updated_option', 'muxvideo_handle_option_update', 10 ); muxvideo_add_field_to_options( $new_value ); add_action( 'updated_option', 'muxvideo_handle_option_update', 10, 3 ); } } add_action( 'updated_option', 'muxvideo_handle_option_update', 10, 3 ); function muxvideo_handle_option_create( $option, $value ) { muxvideo_handle_option_update( $option, [], $value ); } add_action( 'added_option', 'muxvideo_handle_option_create', 10, 3 ); /** * Create signing key */ function muxvideo_create_signing_key() { $apiInstance = muxvideo_get_signing_key_api(); $signing_data = []; try { $signing_key_data = $apiInstance->createUrlSigningKeyWithHttpInfo(); //create a signingkey and recieve the data; $signing_data['id'] = $signing_key_data[0]->getData()->getId(); $signing_data['private_key'] = $signing_key_data[0]->getData()->getPrivateKey(); return $signing_data; } catch (Exception $e) { return ''; } } /** * Generate a JWT for a signed asset */ function muxvideo_get_json_web_token( $playback_id, $key_id, $key_secret, $aud, $playback_restriction_id, $time = 2 ) { $payload = array( "sub" => $playback_id, "aud" => $aud, // v = video, t = thumbnail, g = gif. "exp" => time() + 1800, // Expiry time in epoch - in this case now + 30 mins "kid" => $key_id, "time" => $time ); if ( ! empty( $playback_restriction_id ) ) { $payload['playback_restriction_id'] = $playback_restriction_id; } if ( $aud === 't' ) : $params = array( "width" => 258, "height" => 160, "fit_mode" => "smartcrop", "time" => $time ); $payload = array_merge( $params, $payload ); endif; return JWT::encode( $payload, base64_decode( $key_secret ), 'RS256' ); } /** * Get playback restrictions */ function muxvideo_get_data_playback_restrictions() { $cache_key = 'muxvideo_playback_restrictions'; $cached = get_transient($cache_key); if ($cached !== false) { return $cached; } try { $api = muxvideo_api_client_init_playback_restrictions(); if (!$api) return null; $response = $api->listPlaybackRestrictions(); $all_restrictions = method_exists($response, 'getData') ? $response->getData() : ($response->data ?? []); if (!is_array($all_restrictions) || count($all_restrictions) === 0) { delete_transient($cache_key); return null; } $restriction = $all_restrictions[0]; $restriction_id = method_exists($restriction, 'getId') ? $restriction->getId() : ($restriction->id ?? ''); $ref = method_exists($restriction, 'getReferrer') ? $restriction->getReferrer() : ($restriction->referrer ?? null); $allowed_domains = []; $allow_no_referrer = false; if ($ref) { $allowed_domains = method_exists($ref, 'getAllowedDomains') ? $ref->getAllowedDomains() : ($ref->allowed_domains ?? []); $allow_no_referrer = method_exists($ref, 'getAllowNoReferrer') ? $ref->getAllowNoReferrer() : ($ref->allow_no_referrer ?? false); } $obj = (object) [ 'id' => $restriction_id, 'allowed_domains' => $allowed_domains, 'allow_no_referrer' => $allow_no_referrer, ]; set_transient($cache_key, $obj, HOUR_IN_SECONDS); return $obj; } catch (\Throwable $th) { if (defined('WP_DEBUG') && WP_DEBUG && $th->getCode() != 401) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- guarded by WP_DEBUG, intended for plugin debugging. error_log('Exception muxvideo_get_data_playback_restrictions: ' . $th->getMessage()); } return null; } } function muxvideo_get_header() { include_once MUXVIDEO_PLUGIN_DIR . '/views/templates/header.php'; } function muxvideo_get_sidebar() { include_once MUXVIDEO_PLUGIN_DIR . '/views/templates/sidebar.php'; } function muxvideo_get_pro_version_banner() { include_once MUXVIDEO_PLUGIN_DIR . '/views/templates/pro_version_banner.php'; } function muxvideo_get_contact_container() { include_once MUXVIDEO_PLUGIN_DIR . '/views/templates/contact_container.php'; } function muxvideo_display_player_options() { ob_start(); include_once MUXVIDEO_PLUGIN_DIR . '/views/template-parts/customize-player-options.php'; return ob_get_clean(); } function muxvideo_display_player_other_data( $asset_data, $current_img ) { ob_start(); include_once MUXVIDEO_PLUGIN_DIR . '/views/template-parts/customize-player-other_data.php'; return ob_get_clean(); } include_once MUXVIDEO_PLUGIN_DIR . '/includes/functions-wp_ajax.php'; include_once MUXVIDEO_PLUGIN_DIR . '/views/template-parts/asset-list_table.php'; function muxvideo_authorization_check() { $options = get_option('muxvideo_options', []); $username = isset($options['muxvideo_token_id']) ? mux_maybe_decrypt($options['muxvideo_token_id']) : ''; $password = isset($options['muxvideo_token_secret']) ? mux_maybe_decrypt($options['muxvideo_token_secret']) : ''; if (empty($username) || empty($password)) { return 400; } try { $config = MuxPhp\Configuration::getDefaultConfiguration() ->setUsername($username) ->setPassword($password); $api = new MuxPhp\Api\AssetsApi( new GuzzleHttp\Client(), $config ); $api->listAssets(); return 200; } catch (MuxPhp\ApiException $e) { $code = $e->getCode(); return $code ?: 500; } catch (Exception $e) { return 500; } } add_action( 'admin_enqueue_scripts', 'muxvideo_enqueue_admin_scripts' ); add_action( 'enqueue_block_editor_assets', 'muxvideo_enqueue_admin_scripts' ); function muxvideo_enqueue_admin_scripts() { $nonce = wp_create_nonce( 'mux_token_nonce' ); $data = [ 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => $nonce, ]; wp_localize_script( 'muxvideo-functions-script', 'muxvideoData', $data ); wp_localize_script( 'mux-video-gutenberg-block', 'muxvideoData', $data ); } function muxvideo_enqueue_script_player() { /** * Mux player: https://www.mux.com/player * Url file: https://cdn.jsdelivr.net/npm/@mux/upchunk@3.3.2/dist/upchunk.min.js */ wp_register_script( 'muxvideo-script-player', plugin_dir_url( __FILE__ ) . '../assets/lib/mux-player-2.3.2.min.js', array( 'jquery' ), null, true ); wp_enqueue_script( 'muxvideo-script-player' ); } function muxvideo_enqueue_script_upchunk() { /** * Mux Upchunk: https://github.com/muxinc/upchunk * Url file: https://cdn.jsdelivr.net/npm/@mux/upchunk@3.3.2/dist/upchunk.min.js */ wp_register_script( 'muxvideo-script-upchunk', plugin_dir_url( __FILE__ ) . '../assets/lib/upchunk-3.3.2.min.js', array( 'jquery' ), null, true ); wp_enqueue_script( 'muxvideo-script-upchunk' ); } /** * Hide native WP notices */ function muxvideo_hide_update_nag() { if ( IS_MUXVIDEO_PLUGIN ) : remove_action( 'admin_notices', 'update_nag', 3 ); endif; } add_action( 'admin_menu', 'muxvideo_hide_update_nag' ); /** * Add notice if the plugin is not configured */ function muxvideo_add_configuration_notice() { global $auth_response; if ( $auth_response === 401 ) { ?>
[ "content" => ["type" => 'string' ], "muxSidebar" => ["type" => 'string' ], "playbackIds" => ["type" => 'array' ], "isAutoplay" => ["type" => 'array' ], "isMute" => ["type" => 'array' ], "hasHotkeys" => ["type" => 'array' ], "isLooping" => ["type" => 'array' ], "hasControls" => ["type" => 'array' ], "hasCaptions" => ["type" => 'array' ], "hasSeekButtons" => ["type" => 'array' ], "seekButtonsDuration" => ["type" => 'array' ], "aspectRatio" => ["type" => 'array' ], "maxResolution" => ["type" => 'array' ], "thumbnailTime" => ["type" => 'array' ], "startTime" => ["type" => 'array' ], "maxDuration" => ["type" => 'array' ], "fullWidth" => ["type" => 'array' ], "maxWidth" => ["type" => 'array' ], "jwt" => ["type" => 'array' ], "policy" => ["type" => 'array'], "poster" => ["type" => 'array'], "thumbnailToken" => ["type" => 'array'], "assetTitle" => ["type" => 'array'], ], 'editor_script' => 'mux-video-gutenberg-block', 'render_callback' => 'mux_plugin_renderer', ]); } function mux_plugin_renderer($attributes) { if (!isset($attributes['playbackIds']) || empty($attributes['playbackIds'])) { return '

No videos selected.

'; } $output = ''; $playback_restrictions_data = muxvideo_get_data_playback_restrictions(); $options = get_option('muxvideo_options'); $key_id = $options['muxvideo_signing_data']['id']; $key_secret = $options['muxvideo_signing_data']['private_key']; foreach ($attributes['playbackIds'] as $index => $playback_id) { $token = ''; $thumbnailToken = ''; if (!empty($playback_id) && $attributes['policy'][$index] === 'signed') { // Token para reproducción (aud = 'v') $payload = [ "sub" => $playback_id, "aud" => 'v', "exp" => time() + 600, "kid" => $key_id, "playback_restriction_id" => $playback_restrictions_data->id, ]; $token = JWT::encode($payload, base64_decode($key_secret), 'RS256'); // Token para thumbnail (aud = 't') $thumbnailTime = isset($attributes['thumbnailTime'][$index]) ? intval($attributes['thumbnailTime'][$index]) : 1; $thumbPayload = [ "sub" => $playback_id, "aud" => 't', "exp" => time() + 600, "kid" => $key_id, "playback_restriction_id" => $playback_restrictions_data->id, "time" => $thumbnailTime, ]; $thumbnailToken = JWT::encode($thumbPayload, base64_decode($key_secret), 'RS256'); } // Atributos del player $autoplay = isset($attributes['isAutoplay'][$index]) && $attributes['isAutoplay'][$index] ? 'autoplay="any"' : ''; $muted = isset($attributes['isMute'][$index]) && $attributes['isMute'][$index] ? 'muted' : ''; $loop = isset($attributes['isLooping'][$index]) && $attributes['isLooping'][$index] ? 'loop' : ''; $controls = isset($attributes['hasControls'][$index]) && !$attributes['hasControls'][$index] ? '--controls: none;' : ''; $seekButtons = isset($attributes['hasSeekButtons'][$index]) && !$attributes['hasSeekButtons'][$index] ? '--seek-backward-button: none; --seek-forward-button: none;' : ''; $aspect = isset($attributes['aspectRatio'][$index]) ? $attributes['aspectRatio'][$index] : '16 / 9'; $aspectClass = ($aspect === '1 / 1') ? 'aspect-ratio-1-1' : (($aspect === '4 / 5') ? 'aspect-ratio-9-16' : 'aspect-ratio-16-9'); $resolution = isset($attributes['maxResolution'][$index]) && $attributes['maxResolution'][$index] === '720p' ? '720p' : '1080p'; $width = (!empty($attributes['fullWidth'][$index]) && empty($attributes['maxWidth'][$index])) ? '100%' : esc_attr($attributes['maxWidth'][$index]); $thumbnailTime = isset($attributes['thumbnailTime'][$index]) ? $attributes['thumbnailTime'][$index] : 0; $seekDuration = isset($attributes['seekButtonsDuration'][$index]) ? $attributes['seekButtonsDuration'][$index] : 5; $startTime = isset($attributes['startTime'][$index]) ? $attributes['startTime'][$index] : 0; $assetTitle = isset($attributes['assetTitle'][$index]) && $attributes['assetTitle'][$index] ? $attributes['assetTitle'][$index] : ''; // Manejo del poster (si no está definido, lo generamos firmado) $poster = isset($attributes['poster'][$index]) ? $attributes['poster'][$index] : ''; if (empty($poster) && !empty($thumbnailToken)) { $poster = "https://image.mux.com/{$playback_id}/thumbnail.jpg?token={$thumbnailToken}"; // Persistir el poster generado dentro del bloque: $attributes['poster'][$index] = $poster; } // Persistir también el thumbnail-token $attributes['thumbnailToken'][$index] = $thumbnailToken; $output .= sprintf( '', esc_attr($aspectClass), esc_attr($assetTitle), esc_attr($playback_id), esc_attr($token), esc_attr($poster), esc_attr($thumbnailToken), esc_attr($controls), esc_attr($seekButtons), esc_attr($width), esc_attr($resolution), isset($attributes['hasCaptions'][$index]) && $attributes['hasCaptions'][$index] ? 'true' : 'false', esc_attr($thumbnailTime), esc_attr($startTime), esc_attr($seekDuration), esc_attr($seekDuration), $autoplay, $muted, $loop ); } return '
' . $output . '
'; } add_action( 'init', 'mux_plugin_register_block'); add_action('wp_ajax_muxvideo_get_token', 'muxvideo_get_token_callback'); function muxvideo_get_token_callback() { if ( ! check_ajax_referer( 'mux_token_nonce', '_ajax_nonce', false ) ) { wp_send_json_error( 'Security verification failed', 403 ); } if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( 'Insufficient permission', 403 ); } if (!isset($_POST['playback_id'])) { wp_send_json_error('Missing playback_id'); } $playback_id = sanitize_text_field( wp_unslash( $_POST['playback_id'] ) ); $aud = isset( $_POST['aud'] ) && 't' === $_POST['aud'] ? 't' : 'v'; $time = isset($_POST['time']) ? intval($_POST['time']) : 0; static $key_id = null; static $key_secret = null; static $playback_restrictions_data = null; if ($key_id === null || $key_secret === null) { $options = get_option('muxvideo_options'); $key_id = $options['muxvideo_signing_data']['id']; $key_secret = $options['muxvideo_signing_data']['private_key']; } if ($playback_restrictions_data === null) { $playback_restrictions_data = muxvideo_get_data_playback_restrictions(); } $payload = [ "sub" => $playback_id, "aud" => $aud, "exp" => time() + 1800, "kid" => $key_id, "playback_restriction_id" => $playback_restrictions_data->id, ]; if ($aud === 't' && $time > 0) { $payload["time"] = $time; } $token = JWT::encode($payload, base64_decode($key_secret), 'RS256'); wp_send_json_success(['token' => $token]); } /** Add disclaimer to all pages in the footer */ function muxvideo_add_disclaimer_in_footer() { return '

' . esc_html__( 'common_disclaimer_text', '2coders-integration-mux-video' ) . '

'; } // Programa el reintento con wp_schedule_single_event function muxvideo_update_asset_metadata_later($upload_id, $attempt = 1) { if ($attempt > 10) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- guarded by WP_DEBUG, intended for plugin debugging. error_log("MuxVideo: update metadata failed for upload_id {$upload_id} after 10 attempts"); set_transient("muxvideo_failed_{$upload_id}", true, HOUR_IN_SECONDS); return; } $crons = _get_cron_array(); if (!empty($crons)) { foreach ($crons as $timestamp => $hooks) { if (!empty($hooks['muxvideo_delayed_metadata_update'])) { foreach ($hooks['muxvideo_delayed_metadata_update'] as $key => $data) { if (isset($data['args'][0]) && $data['args'][0] === $upload_id) { wp_unschedule_event($timestamp, 'muxvideo_delayed_metadata_update', $data['args']); } } } } } wp_schedule_single_event( time() + 10, 'muxvideo_delayed_metadata_update', [$upload_id, $attempt] ); } // Acción hookeada que se ejecuta cuando wp_schedule_single_event lo llama add_action('muxvideo_delayed_metadata_update', 'muxvideo_try_update_asset_metadata', 10, 2); function muxvideo_try_update_asset_metadata($upload_id, $attempt) { try { $api = muxvideo_api_client_init(); $assets = $api->listAssets(25, 1, null, $upload_id); if (!empty($assets->getData()) && count($assets->getData()) > 0) { $asset = $assets->getData()[0]; $asset_id = $asset->getId(); $api->updateAsset($asset_id, [ 'meta' => [ 'title' => 'Título actualizado desde reintento' ] ]); } else { muxvideo_update_asset_metadata_later($upload_id, $attempt + 1); } } catch (Exception $e) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- guarded by WP_DEBUG, intended for plugin debugging. error_log("Error: " . $e->getMessage()); } echo '
' . esc_html__('error_try_again', '2coders-integration-mux-video') . '
'; } } function muxvideo_enqueue_settings_script( $hook_suffix = '' ) { if ( ! current_user_can( 'manage_options' ) ) { return; } $is_settings_page = false; if ( function_exists( 'get_current_screen' ) ) { $screen = get_current_screen(); if ( $screen && isset( $screen->id ) && false !== strpos( $screen->id, 'muxvideo-settings' ) ) { $is_settings_page = true; } } if ( ! $is_settings_page ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page-slug detection during admin_enqueue_scripts. $requested_page = isset( $_REQUEST['page'] ) ? sanitize_key( wp_unslash( $_REQUEST['page'] ) ) : ''; if ( 'muxvideo-settings' === $requested_page ) { $is_settings_page = true; } } if ( ! $is_settings_page ) { return; } if ( ! wp_script_is( 'muxvideo-settings-script', 'registered' ) && ! wp_script_is( 'muxvideo-settings-script', 'enqueued' ) ) { return; } $opts = get_option('muxvideo_options', []); $token_id = isset($opts['muxvideo_token_id']) ? mux_maybe_decrypt($opts['muxvideo_token_id']) : ''; $token_secret = isset($opts['muxvideo_token_secret']) ? mux_maybe_decrypt($opts['muxvideo_token_secret']) : ''; wp_localize_script('muxvideo-settings-script', 'MuxVideoSettings', [ 'tokenId' => $token_id, 'tokenSecret' => $token_secret, ]); } add_action('admin_enqueue_scripts', 'muxvideo_enqueue_settings_script', 20);