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