type ) && 'video' !== $data->type ) { return $html; } $iframe_src = oembed_html2src( $data ); if ( is_wp_error( $iframe_src ) ) { $data->arve_error_iframe_src = $iframe_src->get_error_message(); } else { $data->arve_iframe_src = $iframe_src; } $data->provider = sane_provider_name( $data->provider_name ); $data->arve_url = $url; $data->arve_cachetime = current_datetime()->format( DateTime::ATOM ); if ( ! empty( $data->upload_date ) ) { $atom_upload_date = normalize_datetime_to_atom( $data->upload_date, 'UTC' ); if ( $atom_upload_date !== $data->upload_date ) { $data->upload_data_org = $data->upload_date; $data->upload_date = $atom_upload_date; } } if ( function_exists( __NAMESPACE__ . '\Pro\oembed_data' ) ) { Pro\oembed_data( $data ); } if ( function_exists( __NAMESPACE__ . '\Privacy\oembed_data' ) ) { Privacy\oembed_data( $data ); } unset( $data->html ); $attr = array(); foreach ( $data as $key => $value ) { $attr[ 'data-' . $key ] = $value; } $html .= PHP_EOL . PHP_EOL; $html .= first_tag_attr( '', $attr ); return $html; } /** * Sanitizes the provider name by removing special characters and converting to lowercase. * * @param string $provider The provider name to be sanitized. * @return string The sanitized provider name. */ function sane_provider_name( string $provider ): string { $provider = preg_replace( '/[^a-z0-9]/', '', strtolower( $provider ) ); $provider = str_replace( 'wistiainc', 'wistia', $provider ); $provider = str_replace( 'rumblecom', 'rumble', $provider ); return $provider; } /** * Filters the cached oEmbed HTML. * * @see WP_Embed::shortcode() * * @param string|false $cache The cached HTML result, stored in post meta. * @param string $url The attempted embed URL. * @param array $attr An array of shortcode attributes. * @param ?int $post_id Post ID. */ function filter_embed_oembed_html( $cache, string $url, array $attr, ?int $post_id ): string { $oembed_data = extract_oembed_data( $cache ); if ( $oembed_data ) { $a['url'] = $url; $a['oembed_data'] = $oembed_data; $a['origin_data'][ __FUNCTION__ ]['post_id'] = $post_id; $a['origin_data'][ __FUNCTION__ ]['cache'] = delete_oembed_caches_when_missing_data( $oembed_data ); $a['origin_data'][ __FUNCTION__ ]['attr'] = $attr; $cache = build_video( $a ); } return $cache; } function cache_is_old_enough( object $oembed_data ): bool { if ( ! isset( $oembed_data->arve_cachetime ) ) { return false; } $cache_date = DateTime::createFromFormat( DateTime::ATOM, $oembed_data->arve_cachetime ); return $cache_date && ( new DateTime() )->diff( $cache_date )->days > 7; } /** * Delete oEmbed caches when required data is missing from the supplied object. * * The function inspects the `$oembed_data` object for the presence of * `provider`, `arve_cachetime`, and (when the Pro helper is available) for * thumbnail information. If any of those checks fail, the corresponding * cache entry is removed via `delete_oembed_cache()` and a flag is added to * the result array. * * @param object{ * arve_url?: string|null, * provider?: string|null, * arve_cachetime?: int|null, * thumbnail_srcset?: mixed, * thumbnail_large_url?: mixed * } $oembed_data An object (typically a `stdClass`) containing oEmbed fields * * @return array */ function delete_oembed_caches_when_missing_data( object $oembed_data ): array { $pro_active = function_exists( __NAMESPACE__ . '\Pro\oembed_data' ); $result = []; $url = $oembed_data->arve_url ?? false; $provider = $oembed_data->provider ?? false; $cachetime = $oembed_data->arve_cachetime ?? false; if ( ! $provider || ! $cachetime ) { $result['delete_oembed_cache_for_provider_or_cachetime'] = delete_oembed_cache( $url ); } if ( $pro_active && $url && in_array( $provider, [ 'youtube', 'vimeo' ], true ) && ( ! isset( $oembed_data->thumbnail_srcset ) || ! isset( $oembed_data->thumbnail_large_url ) ) ) { $result['delete_cache_for_srcset_or_large_thumbnail'] = delete_oembed_cache( $url ); } return $result; } function extract_oembed_data( string $html ): ?object { $p = new WP_HTML_Tag_Processor( $html ); if ( ! $p->next_tag( [ 'class_name' => 'arve-data' ] ) ) { return null; } $data = (object) []; $data_atts = $p->get_attribute_names_with_prefix( 'data-' ); foreach ( $data_atts as $attr ) { $name = str_replace( 'data-', '', $attr ); $value = $p->get_attribute( $attr ); $data->{$name} = ( 'version' !== $name && is_numeric( $value ) ) ? $value + 0 : $value; } return $data; } /** * Build a srcset attribute string from an array of image URLs keyed by width. * * @param array $sizes An associative array where the key is the * image width (e.g. 300, 600) and the value * is the URL of the image at that size. * * @return string A comma‑separated srcset string suitable for the HTML * `srcset` attribute, e.g. * "https://example.com/300.jpg 300w, https://example.com/600.jpg 600w". * Returns an empty string when `$sizes` is empty. */ function yt_srcset( array $sizes ): string { if ( ! empty( $sizes ) ) { foreach ( $sizes as $size => $url ) { $srcset_comb[] = "$url {$size}w"; } return implode( ', ', $srcset_comb ); } return ''; } /** * Generates the source URL from the oEmbed HTML data. * * @param object $data The oEmbed HTML data. * @return string|WP_Error The source URL generated from the oEmbed HTML data. */ function oembed_html2src( object $data ) { if ( empty( $data->html ) ) { return new WP_Error( 'no-oembed-html', __( 'No oembed html', 'advanced-responsive-video-embedder' ) ); } $html = htmlspecialchars_decode( $data->html, ENT_COMPAT | ENT_HTML5 ); if ( 'TikTok' === $data->provider_name ) { $tiktok_video_id = get_attribute_from_html_tag( array( 'class' => 'tiktok-embed' ), 'data-video-id', $html ); if ( $tiktok_video_id ) { return 'https://www.tiktok.com/embed/v2/' . $tiktok_video_id; } else { return new WP_Error( 'tiktok-video-id', __( 'Failed to extract tiktok video id from oembed html', 'advanced-responsive-video-embedder' ), $html ); } } elseif ( 'Facebook' === $data->provider_name ) { $facebook_video_url = get_attribute_from_html_tag( array( 'class' => 'fb-video' ), 'data-href', $html ); if ( $facebook_video_url ) { return 'https://www.facebook.com/plugins/video.php?href=' . rawurlencode( $facebook_video_url ); } else { return new WP_Error( 'facebook-video-id', __( 'Failed to extract facebook video url from this html', 'advanced-responsive-video-embedder' ), $html ); } } else { $iframe_src = get_attribute_from_html_tag( array( 'tag_name' => 'iframe' ), 'src', $html ); if ( $iframe_src ) { $iframe_src = valid_url( $iframe_src ); if ( $iframe_src ) { return $iframe_src; } else { return new WP_Error( 'facebook-video-id', __( 'Invalid iframe src url', 'advanced-responsive-video-embedder' ), $html ); } } else { return new WP_Error( 'iframe-src', __( 'Failed to extract iframe src from this html', 'advanced-responsive-video-embedder' ), $html ); } } } /** * Add a Referer header for Vimeo URLs. * * @param array $args Request arguments. * @param string $url The URL that will be fetched. * * @return array The same structure as $args, with * $args['headers']['Referer'] set to * site_url() when $url contains “vimeo”. */ function vimeo_referer( array $args, string $url ): array { if ( str_contains( $url, 'vimeo' ) ) { $args['headers']['Referer'] = site_url(); } return $args; } function remove_youtube_si_param( string $provider, string $url ): string { if ( str_starts_with( $provider, 'https://www.youtube.com' ) ) { $url = remove_query_arg( 'si', $url ); $provider = add_query_arg( 'url', urlencode( $url ), $provider ); // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode } return $provider; }