PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.8.1
Jetpack – WP Security, Backup, Speed, & Growth v14.8.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / shortcodes / youtube.php
jetpack / modules / shortcodes Last commit date
css 1 year ago images 2 years ago img 2 years ago js 1 year ago archiveorg-book.php 5 years ago archiveorg.php 5 years ago archives.php 5 years ago bandcamp.php 3 years ago brightcove.php 5 years ago cartodb.php 5 years ago class.filter-embedded-html-objects.php 2 years ago codepen.php 5 years ago crowdsignal.php 1 year ago dailymotion.php 3 years ago descript.php 4 years ago facebook.php 1 year ago flatio.php 5 years ago flickr.php 1 year ago getty.php 2 years ago gist.php 1 year ago googleapps.php 2 years ago googlemaps.php 2 years ago googleplus.php 5 years ago gravatar.php 2 years ago houzz.php 5 years ago inline-pdfs.php 4 years ago instagram.php 1 year ago kickstarter.php 3 years ago mailchimp.php 3 years ago medium.php 3 years ago mixcloud.php 2 years ago others.php 1 year ago pinterest.php 2 years ago presentations.php 2 years ago quiz.php 4 years ago recipe.php 1 year ago scribd.php 5 years ago sitemap.php 5 years ago slideshare.php 5 years ago slideshow.php 1 year ago smartframe.php 3 years ago soundcloud.php 3 years ago spotify.php 2 years ago ted.php 5 years ago tweet.php 2 years ago twitchtv.php 5 years ago twitter-timeline.php 5 years ago twitter.php 1 year ago unavailable.php 3 years ago untappd-menu.php 3 years ago upcoming-events.php 1 year ago ustream.php 5 years ago videopress.php 4 years ago vimeo.php 2 years ago vine.php 5 years ago vr.php 2 years ago wufoo.php 3 years ago youtube.php 1 year ago
youtube.php
666 lines
1 <?php
2 /**
3 * Youtube shortcode
4 *
5 * Contains shortcode + some improvements over the Core Embeds syntax (see http://codex.wordpress.org/Embeds )
6 *
7 * Examples:
8 * [youtube https://www.youtube.com/watch?v=WVbQ-oro7FQ]
9 * [youtube=http://www.youtube.com/watch?v=wq0rXGLs0YM&fs=1&hl=bg_BG&autohide=1&rel=0]
10 * http://www.youtube.com/watch?v=H2Ncxw1xfck&w=320&h=240&fmt=1&rel=0&showsearch=1&hd=0
11 * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
12 * https://www.youtube.com/playlist?list=PLP7HaNDU4Cifov7C2fQM8Ij6Ew_uPHEXW
13 *
14 * @package automattic/jetpack
15 */
16
17 /**
18 * Replaces YouTube embeds with YouTube shortcodes.
19 *
20 * Covers the following formats:
21 * 2008-07-15:
22 * <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/bZBHZT3a-FA&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/bZBHZT3a-FA&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>
23 * around 2008-06-06 youtube changed their old embed code to this:
24 * <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/M1D30gS7Z8U&hl=en"></param><embed src="http://www.youtube.com/v/M1D30gS7Z8U&hl=en" type="application/x-shockwave-flash" width="425" height="344"></embed></object>
25 * old style was:
26 * <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/dGY28Qbj76A&rel=0"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/dGY28Qbj76A&rel=0" type="application/x-shockwave-flash" wmode="transparent" width="425" height="344"></embed></object>
27 * 12-2010:
28 * <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/3H8bnKdf654?fs=1&amp;hl=en_GB"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/3H8bnKdf654?fs=1&amp;hl=en_GB" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
29 * 01-2011:
30 * <iframe title="YouTube video player" class="youtube-player" width="640" height="390" src="http://www.youtube.com/embed/Qq9El3ki0_g" frameborder="0" allowFullScreen></iframe>
31 * <iframe class="youtube-player" width="640" height="385" src="http://www.youtube.com/embed/VIDEO_ID" frameborder="0"></iframe>
32 *
33 * @param string $content HTML content.
34 * @return string The content with YouTube embeds replaced with YouTube shortcodes.
35 */
36 function jetpack_youtube_embed_to_short_code( $content ) {
37 if ( ! is_string( $content ) || ! str_contains( $content, 'youtube.com' ) ) {
38 return $content;
39 }
40
41 // older codes.
42 $regexp = '!<object(.*?)>.*?<param\s+name=[\'"]movie[\'"]\s+value=[\'"](https?:)?//www\.youtube\.com/v/([^\'"]+)[\'"].*?>.*?</object>!i';
43 $regexp_ent = htmlspecialchars( $regexp, ENT_NOQUOTES );
44 $old_regexp = '!<embed(?:\s+\w+="[^"]*")*\s+src="https?(?:\:|&#0*58;)//www\.youtube\.com/v/([^"]+)"(?:\s+\w+="[^"]*")*\s*(?:/>|>\s*</embed>)!';
45 $old_regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $old_regexp, ENT_NOQUOTES ) );
46
47 // new code.
48 $ifr_regexp = '!<iframe((?:\s+\w+="[^"]*")*?)\s+src="(https?:)?//(?:www\.)*youtube.com/embed/([^"]+)".*?</iframe>!i';
49 $ifr_regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $ifr_regexp, ENT_NOQUOTES ) );
50
51 foreach ( compact( 'regexp', 'regexp_ent', 'old_regexp', 'old_regexp_ent', 'ifr_regexp', 'ifr_regexp_ent' ) as $reg => $regexp ) {
52 if ( ! preg_match_all( $regexp, $content, $matches, PREG_SET_ORDER ) ) {
53 continue;
54 }
55
56 foreach ( $matches as $match ) {
57 /*
58 * Hack, but '?' should only ever appear once, and
59 * it should be for the 1st field-value pair in query string,
60 * if it is present
61 * YouTube changed their embed code.
62 * Example of how it is now:
63 * <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/aP9AaD4tgBY?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/aP9AaD4tgBY?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
64 * As shown at the start of function, previous YouTube didn't '?'
65 * the 1st field-value pair.
66 */
67 if ( in_array( $reg, array( 'ifr_regexp', 'ifr_regexp_ent', 'regexp', 'regexp_ent' ), true ) ) {
68 $params = $match[1];
69
70 if ( in_array( $reg, array( 'ifr_regexp_ent', 'regexp_ent' ), true ) ) {
71 $params = html_entity_decode( $params, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
72 }
73
74 $params = wp_kses_hair( $params, array( 'http' ) );
75
76 $width = isset( $params['width'] ) ? (int) $params['width']['value'] : 0;
77 $height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0;
78 $wh = '';
79
80 if ( $width && $height ) {
81 $wh = "&w=$width&h=$height";
82 }
83
84 $url = esc_url_raw( "https://www.youtube.com/watch?v={$match[3]}{$wh}" );
85 } else {
86 $match[1] = str_replace( '?', '&', $match[1] );
87
88 $url = esc_url_raw( 'https://www.youtube.com/watch?v=' . html_entity_decode( $match[1], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ) );
89 }
90
91 $content = str_replace( $match[0], "[youtube $url]", $content );
92
93 /**
94 * Fires before the YouTube embed is transformed into a shortcode.
95 *
96 * @module shortcodes
97 *
98 * @since 1.2.0
99 *
100 * @param string youtube Shortcode name.
101 * @param string $url YouTube video URL.
102 */
103 do_action( 'jetpack_embed_to_shortcode', 'youtube', $url );
104 }
105 }
106
107 return $content;
108 }
109 add_filter( 'pre_kses', 'jetpack_youtube_embed_to_short_code' );
110
111 /**
112 * Replaces plain-text links to YouTube videos with YouTube embeds.
113 *
114 * @param string $content HTML content.
115 *
116 * @return string The content with embeds instead of URLs
117 */
118 function jetpack_youtube_link( $content ) {
119 return jetpack_preg_replace_callback_outside_tags( '!(?:\n|\A)https?://(?:www\.)?(?:youtube.com/(?:v/|playlist|watch[/\#?])|youtu\.be/)[^\s]+?(?:\n|\Z)!i', 'jetpack_youtube_link_callback', $content, 'youtube.com/' );
120 }
121
122 /**
123 * Callback function for the regex that replaces YouTube URLs with
124 * YouTube embeds.
125 *
126 * @param array $matches An array containing a YouTube URL.
127 */
128 function jetpack_youtube_link_callback( $matches ) {
129 return "\n" . jetpack_youtube_id( $matches[0] ) . "\n";
130 }
131
132 /**
133 * Normalizes a YouTube URL to include a v= parameter and a query string free of encoded ampersands.
134 *
135 * @param string|array $url Youtube URL.
136 * @return string|false The normalized URL or false if input is invalid.
137 */
138 if ( ! function_exists( 'jetpack_youtube_sanitize_url' ) ) :
139 /**
140 * Clean up Youtube URL to match a single format.
141 *
142 * @param string|array $url Youtube URL.
143 */
144 function jetpack_youtube_sanitize_url( $url ) {
145 if ( is_array( $url ) && isset( $url['url'] ) ) {
146 $url = $url['url'];
147 }
148 if ( ! is_string( $url ) ) {
149 return false;
150 }
151
152 $url = trim( $url, ' "' );
153 $url = trim( $url );
154 $url = str_replace( array( 'youtu.be/', '/v/', '#!v=', '&amp;', '&#038;', 'playlist' ), array( 'youtu.be/?v=', '/?v=', '?v=', '&', '&', 'videoseries' ), $url );
155
156 // Replace any extra question marks with ampersands - the result of a URL like "https://www.youtube.com/v/dQw4w9WgXcQ?fs=1&hl=en_US" being passed in.
157 $query_string_start = strpos( $url, '?' );
158
159 if ( false !== $query_string_start ) {
160 $url = substr( $url, 0, $query_string_start + 1 ) . str_replace( '?', '&', substr( $url, $query_string_start + 1 ) );
161 }
162
163 return $url;
164 }
165 endif;
166
167 /**
168 * Converts a YouTube URL into an embedded YouTube video.
169 *
170 * URL can be:
171 * http://www.youtube.com/embed/videoseries?list=PL94269DA08231042B&amp;hl=en_US
172 * http://www.youtube.com/watch#!v=H2Ncxw1xfck
173 * http://www.youtube.com/watch?v=H2Ncxw1xfck
174 * http://www.youtube.com/watch?v=H2Ncxw1xfck&w=320&h=240&fmt=1&rel=0&showsearch=1&hd=0
175 * http://www.youtube.com/v/jF-kELmmvgA
176 * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
177 * http://youtu.be/Rrohlqeir5E
178 * https://www.youtube.com/watch?v=GJNxoe-iSb4&list=PLAVZ4NFtZX0fE54mDSqNKym-o_rz-8xmk
179 *
180 * @param string $url Youtube URL.
181 */
182 function jetpack_youtube_id( $url ) {
183 $id = jetpack_get_youtube_id( $url );
184
185 if ( ! $id ) {
186 return sprintf( '<!--%s-->', esc_html__( 'YouTube Error: bad URL entered', 'jetpack' ) );
187 }
188
189 $url = jetpack_youtube_sanitize_url( $url );
190 $url = wp_parse_url( $url );
191
192 $thumbnail = "https://i.ytimg.com/vi/$id/hqdefault.jpg";
193 $video_url = add_query_arg( 'v', $id, 'https://www.youtube.com/watch' );
194
195 $args = jetpack_shortcode_youtube_args( $url );
196 if ( empty( $args ) ) {
197 return sprintf( '<!--%s-->', esc_html__( 'YouTube Error: empty URL args', 'jetpack' ) );
198 }
199
200 // Account for URL having both v and list, where jetpack_get_youtube_id() only accounts for one or the other.
201 if ( isset( $args['list'] ) && $args['list'] === $id ) {
202 $id = null;
203 }
204 if ( isset( $args['v'] ) ) {
205 $id = $args['v'];
206 }
207 if ( ! $id && empty( $args['list'] ) ) {
208 return sprintf( '<!--%s-->', esc_html__( 'YouTube Error: missing id and/or list', 'jetpack' ) );
209 }
210
211 list( $w, $h ) = jetpack_shortcode_youtube_dimensions( $args );
212
213 $params = array(
214 'rel' => ( isset( $args['rel'] ) && '0' === $args['rel'] ) ? 0 : 1,
215 'showsearch' => ( isset( $args['showsearch'] ) && '1' === $args['showsearch'] ) ? 1 : 0, // Now deprecated. See https://developers.google.com/youtube/player_parameters#march-29,-2012.
216 'showinfo' => ( isset( $args['showinfo'] ) && '0' === $args['showinfo'] ) ? 0 : 1, // Now obsolete. See https://developers.google.com/youtube/player_parameters#showinfo.
217 'iv_load_policy' => ( isset( $args['iv_load_policy'] ) && '3' === $args['iv_load_policy'] ) ? 3 : 1,
218 'fs' => 1,
219 'hl' => str_replace( '_', '-', get_locale() ),
220 );
221 if ( isset( $args['fmt'] ) && (int) $args['fmt'] ) {
222 $params['fmt'] = (int) $args['fmt']; // Apparently an obsolete parameter. Not referenced on https://developers.google.com/youtube/player_parameters.
223 }
224
225 // The autohide parameter has been deprecated since 2015. See https://developers.google.com/youtube/player_parameters#august-19,-2015.
226 if ( ! isset( $args['autohide'] ) || ( $args['autohide'] < 0 || 2 < $args['autohide'] ) ) {
227 $params['autohide'] = 2;
228 } else {
229 $params['autohide'] = (int) $args['autohide'];
230 }
231
232 $start = 0;
233 if ( isset( $args['start'] ) ) {
234 $start = (int) $args['start'];
235 } elseif ( isset( $args['t'] ) ) {
236 if ( is_numeric( $args['t'] ) ) {
237 $start = (int) $args['t'];
238 } elseif ( is_string( $args['t'] ) ) {
239 $time_pieces = preg_split( '/(?<=\D)(?=\d+)/', $args['t'] );
240
241 foreach ( $time_pieces as $time_piece ) {
242 $int = (int) $time_piece;
243 switch ( substr( $time_piece, - 1 ) ) {
244 case 'h':
245 $start += $int * 3600;
246 break;
247 case 'm':
248 $start += $int * 60;
249 break;
250 case 's':
251 $start += $int;
252 break;
253 }
254 }
255 }
256 }
257 if ( $start ) {
258 $params['start'] = (int) $start;
259 }
260
261 if ( isset( $args['end'] ) && (int) $args['end'] ) {
262 $params['end'] = (int) $args['end'];
263 }
264 if ( isset( $args['hd'] ) && (int) $args['hd'] ) {
265 $params['hd'] = (int) $args['hd']; // Now obsolete per https://developers.google.com/youtube/player_parameters#march-29,-2012.
266 }
267 if ( isset( $args['vq'] ) && in_array( $args['vq'], array( 'hd720', 'hd1080' ), true ) ) {
268 $params['vq'] = $args['vq']; // Note, this appears to be obsolete. Not referenced on https://developers.google.com/youtube/player_parameters.
269 }
270 if ( isset( $args['cc_load_policy'] ) ) {
271 $params['cc_load_policy'] = 1;
272 }
273 if ( isset( $args['cc_lang_pref'] ) ) {
274 $params['cc_lang_pref'] = preg_replace( '/[^_a-z0-9-]/i', '', $args['cc_lang_pref'] );
275 }
276
277 // The wmode parameter appears to be obsolete. Not referenced on https://developers.google.com/youtube/player_parameters.
278 if ( isset( $args['wmode'] ) && in_array( strtolower( $args['wmode'] ), array( 'opaque', 'window', 'transparent' ), true ) ) {
279 $params['wmode'] = $args['wmode'];
280 } else {
281 $params['wmode'] = 'transparent';
282 }
283
284 // The theme parameter is obsolete per https://developers.google.com/youtube/player_parameters#august-19,-2015.
285 if ( isset( $args['theme'] ) && in_array( strtolower( $args['theme'] ), array( 'dark', 'light' ), true ) ) {
286 $params['theme'] = $args['theme'];
287 }
288
289 if ( isset( $args['list'] ) ) {
290 $params['listType'] = 'playlist';
291 $params['list'] = preg_replace( '|[^_a-z0-9-]|i', '', $args['list'] );
292 }
293
294 /**
295 * Allow YouTube videos to start playing automatically.
296 *
297 * @module shortcodes
298 *
299 * @since 2.2.2
300 *
301 * @param bool false Enable autoplay for YouTube videos.
302 */
303 if ( apply_filters( 'jetpack_youtube_allow_autoplay', false ) && isset( $args['autoplay'] ) ) {
304 $params['autoplay'] = (int) $args['autoplay'];
305 }
306
307 $is_amp = class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request();
308
309 if ( $is_amp && $id ) {
310 // Note that <amp-youtube> currently is not well suited for playlists that don't have an individual video selected, hence the $id check above.
311 $placeholder = sprintf(
312 '<a href="%1$s" placeholder><amp-img src="%2$s" alt="%3$s" layout="fill" object-fit="cover"><noscript><img src="%2$s" loading="lazy" decoding="async" alt="%3$s"></noscript></amp-img></a>',
313 esc_url( $video_url ),
314 esc_url( $thumbnail ),
315 esc_attr__( 'YouTube Poster', 'jetpack' ) // Would be preferable to provide YouTube video title, but not available in this non-oEmbed context.
316 );
317
318 $html_attributes = array();
319 foreach ( $params as $param_name => $param_value ) {
320 $html_attributes[] = sprintf(
321 'data-param-%s="%s"',
322 sanitize_key( $param_name ),
323 esc_attr( $param_value )
324 );
325 }
326
327 $html = sprintf(
328 '<amp-youtube data-videoid="%s" %s width="%d" height="%d" layout="responsive">%s</amp-youtube>',
329 esc_attr( $id ),
330 implode( ' ', $html_attributes ), // Note: Escaping done above.
331 esc_attr( $w ),
332 esc_attr( $h ),
333 $placeholder
334 );
335 } else {
336 // In AMP, the AMP_Iframe_Sanitizer will convert into <amp-iframe> as required.
337 $src = 'https://www.youtube.com/embed';
338 if ( $id ) {
339 $src .= "/$id";
340 }
341 $src = add_query_arg(
342 array_merge(
343 array( 'version' => 3 ),
344 $params
345 ),
346 $src
347 );
348
349 $layout = $is_amp ? 'layout="responsive" ' : '';
350
351 $html = sprintf(
352 '<iframe class="youtube-player" width="%s" height="%s" %ssrc="%s" allowfullscreen="true" style="border:0;" sandbox="allow-scripts allow-same-origin allow-popups allow-presentation allow-popups-to-escape-sandbox"></iframe>',
353 esc_attr( $w ),
354 esc_attr( $h ),
355 $layout,
356 esc_url( $src )
357 );
358 }
359
360 // Let's do some alignment wonder in a span, unless we're producing a feed.
361 if ( ! is_feed() ) {
362 $alignmentcss = 'text-align:center;';
363 if ( isset( $args['align'] ) ) {
364 switch ( $args['align'] ) {
365 case 'left':
366 $alignmentcss = "float:left; width:{$w}px; height:{$h}px; margin-right:10px; margin-bottom: 10px;";
367 break;
368 case 'right':
369 $alignmentcss = "float:right; width:{$w}px; height:{$h}px; margin-left:10px; margin-bottom: 10px;";
370 break;
371 }
372 }
373
374 $html = sprintf(
375 '<span class="embed-youtube" style="%s display: block;">%s</span>',
376 esc_attr( $alignmentcss ),
377 $html
378 );
379
380 }
381
382 /**
383 * Format output for Calypso Reader/Notifications/Comments
384 */
385 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
386 require_once WP_CONTENT_DIR . '/lib/display-context.php';
387 $context = A8C\Display_Context\get_current_context();
388 if ( A8C\Display_Context\NOTIFICATIONS === $context ) {
389 return sprintf(
390 '<a href="%1$s" target="_blank" rel="noopener noreferrer"><img src="%2$s" alt="%3$s" /></a>',
391 esc_url( $video_url ),
392 esc_url( $thumbnail ),
393 esc_html__( 'YouTube video', 'jetpack' )
394 );
395 }
396 }
397
398 /**
399 * Filter the YouTube video HTML output.
400 *
401 * @module shortcodes
402 *
403 * @since 1.2.3
404 *
405 * @param string $html YouTube video HTML output.
406 */
407 $html = apply_filters( 'video_embed_html', $html );
408
409 return $html;
410 }
411
412 /**
413 * Gets the args present in the YouTube shortcode URL.
414 *
415 * @since 8.0.0
416 *
417 * @param array $url The parsed URL of the shortcode.
418 *
419 * @return array|false The query args of the URL, or false.
420 */
421 function jetpack_shortcode_youtube_args( $url ) {
422 $qargs = array();
423 if ( ! empty( $url['query'] ) ) {
424 wp_parse_str( $url['query'], $qargs );
425 } else {
426 return false;
427 }
428
429 $fargs = array();
430 if ( ! empty( $url['fragment'] ) ) {
431 wp_parse_str( $url['fragment'], $fargs );
432 }
433
434 return array_merge( $fargs, $qargs );
435 }
436
437 /**
438 * Display the Youtube shortcode.
439 *
440 * @param array $atts Shortcode attributes.
441 *
442 * @return string The rendered shortcode.
443 */
444 function jetpack_youtube_shortcode( $atts ) {
445 $url = ( isset( $atts[0] ) ) ? ltrim( $atts[0], '=' ) : shortcode_new_to_old_params( $atts );
446 return jetpack_youtube_id( $url );
447 }
448 add_shortcode( 'youtube', 'jetpack_youtube_shortcode' );
449
450 /**
451 * Gets the dimensions of the [youtube] shortcode.
452 *
453 * Calculates the width and height, taking $content_width into consideration.
454 *
455 * @since 8.0.0
456 *
457 * @param array $query_args The query args of the URL.
458 *
459 * @return array The width and height of the shortcode.
460 */
461 function jetpack_shortcode_youtube_dimensions( $query_args ) {
462 $h = null;
463 global $content_width;
464
465 $input_w = ( isset( $query_args['w'] ) && (int) $query_args['w'] ) ? (int) $query_args['w'] : 0;
466 $input_h = ( isset( $query_args['h'] ) && (int) $query_args['h'] ) ? (int) $query_args['h'] : 0;
467
468 // If we have $content_width, use it.
469 if ( ! empty( $content_width ) ) {
470 $default_width = $content_width;
471 } else {
472 // Otherwise get default width from the old, now deprecated embed_size_w option.
473 $default_width = get_option( 'embed_size_w' );
474 }
475
476 // If we don't know those 2 values use a hardcoded width.
477 if ( empty( $default_width ) ) {
478 $default_width = 640;
479 }
480
481 if ( $input_w > 0 && $input_h > 0 ) {
482 $w = $input_w;
483 $h = $input_h;
484 } elseif ( 0 === $input_w && 0 === $input_h ) {
485 if ( isset( $query_args['fmt'] ) && (int) $query_args['fmt'] ) {
486 $w = ( ! empty( $content_width ) ? min( $content_width, 480 ) : 480 );
487 } else {
488 $w = ( ! empty( $content_width ) ? min( $content_width, $default_width ) : $default_width );
489 $h = ceil( ( $w / 16 ) * 9 );
490 }
491 } elseif ( $input_w > 0 ) {
492 $w = $input_w;
493 $h = ceil( ( $w / 16 ) * 9 );
494 } elseif ( isset( $query_args['fmt'] ) && (int) $query_args['fmt'] ) {
495 $w = ( ! empty( $content_width ) ? min( $content_width, 480 ) : 480 );
496 } else {
497 $w = ( ! empty( $content_width ) ? min( $content_width, $default_width ) : $default_width );
498 $h = $input_h;
499 }
500
501 /**
502 * Filter the YouTube player width.
503 *
504 * @module shortcodes
505 *
506 * @since 1.1.0
507 *
508 * @param int $w Width of the YouTube player in pixels.
509 */
510 $w = (int) apply_filters( 'youtube_width', $w );
511
512 /**
513 * Filter the YouTube player height.
514 *
515 * @module shortcodes
516 *
517 * @since 1.1.0
518 *
519 * @param int $h Height of the YouTube player in pixels.
520 */
521 $h = (int) apply_filters( 'youtube_height', $h );
522
523 return array( $w, $h );
524 }
525
526 /**
527 * For bare URLs on their own line of the form
528 * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
529 *
530 * @param array $matches Regex partial matches against the URL passed.
531 * @param array $attr Attributes received in embed response.
532 * @param string $url Requested URL to be embedded.
533 */
534 function wpcom_youtube_embed_crazy_url( $matches, $attr, $url ) {
535 return jetpack_youtube_id( $url );
536 }
537
538 /**
539 * Get the regex for Youtube URLs.
540 */
541 function wpcom_youtube_get_regex() {
542 return '#https?://(?:www\.)?(?:youtube.com/(?:v/|playlist|watch[/\#?])|youtu\.be/).*#i';
543 }
544
545 /**
546 * Add a new handler to automatically transform custom Youtube URLs (like playlists) into embeds.
547 */
548 function wpcom_youtube_embed_crazy_url_init() {
549 if ( ! defined( 'REST_API_REQUEST' ) ) {
550 return;
551 }
552
553 // Register the custom handler to provide the better support for the private video.
554 wp_embed_register_handler( 'wpcom_youtube_embed_crazy_url', wpcom_youtube_get_regex(), 'wpcom_youtube_embed_crazy_url' );
555 }
556 add_action( 'init', 'wpcom_youtube_embed_crazy_url_init' );
557
558 /**
559 * Filters the oEmbed result before any HTTP requests are made for YouTube.
560 *
561 * @since 13.9
562 *
563 * @param null|string $result The UNSANITIZED (and potentially unsafe) HTML that should be used to embed. Default null.
564 * @param string $url The URL that should be inspected for discovery `<link>` tags.
565 * @param array $args oEmbed remote get arguments.
566 * @return null|string The UNSANITIZED (and potentially unsafe) HTML that should be used to embed.
567 * Null if the URL does not belong to the current site.
568 */
569 function wpcom_youtube_filter_pre_oembed_result( $result, $url, $args ) {
570 // Return early if it's not a YouTube URL.
571 if ( ! preg_match( wpcom_youtube_get_regex(), $url, $matches ) ) {
572 return $result;
573 }
574
575 // Try to get the oembed data by the Core's approach.
576 $wp_oembed = _wp_oembed_get_object();
577 $data = $wp_oembed->get_data( $url, $args );
578 if ( $data ) {
579 /** This filter is documented in wp-includes/class-wp-oembed.php */
580 return apply_filters( 'oembed_result', $wp_oembed->data2html( $data, $url ), $url, $args );
581 }
582
583 // Fallback to the custom handler if the oembed result is not found, especially for the private video.
584 return jetpack_youtube_id( $url );
585 }
586 add_filter( 'pre_oembed_result', 'wpcom_youtube_filter_pre_oembed_result', 10, 3 );
587
588 /**
589 * Remove the ending question mark from the video id of the YouTube URL.
590 *
591 * Example: https://www.youtube.com/watch?v=AVAWwXeOyyQ?
592 *
593 * @since 13.9
594 *
595 * @param string $provider URL of the oEmbed provider.
596 * @param string $url URL of the content to be embedded.
597 *
598 * @return string
599 */
600 function wpcom_youtube_oembed_fetch_url( $provider, $url ) {
601 if ( ! wp_startswith( $provider, 'https://www.youtube.com/oembed' ) ) {
602 return $provider;
603 }
604
605 $parsed = wp_parse_url( $url );
606 if ( ! isset( $parsed['query'] ) ) {
607 return $provider;
608 }
609
610 $query_vars = array();
611 wp_parse_str( $parsed['query'], $query_vars );
612 if ( isset( $query_vars['v'] ) && wp_endswith( $query_vars['v'], '?' ) ) {
613 $url = remove_query_arg( array( 'v' ), $url );
614 $url = add_query_arg( 'v', preg_replace( '/\?$/', '', $query_vars['v'] ), $url );
615 }
616
617 $provider = remove_query_arg( array( 'url' ), $provider );
618 $provider = add_query_arg( 'url', rawurlencode( $url ), $provider );
619
620 return $provider;
621 }
622 add_filter( 'oembed_fetch_url', 'wpcom_youtube_oembed_fetch_url', 10, 2 );
623
624 if (
625 ! is_admin()
626 &&
627 /**
628 * Allow oEmbeds in Jetpack's Comment form.
629 *
630 * @module shortcodes
631 *
632 * @since 2.8.0
633 *
634 * @param int $allow_oembed Option to automatically embed all plain text URLs.
635 */
636 apply_filters( 'jetpack_comments_allow_oembed', true )
637 // No need for this on WordPress.com, this is done for multiple shortcodes at a time there.
638 && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM )
639 ) {
640 /*
641 * We attach wp_kses_post to comment_text in default-filters.php with priority of 10 anyway,
642 * so the iframe gets filtered out.
643 * Higher priority because we need it before auto-link and autop get to it.
644 */
645 add_filter( 'comment_text', 'jetpack_youtube_link', 1 );
646 }
647
648 /**
649 * Core changes to do_shortcode (https://core.trac.wordpress.org/changeset/34747) broke "improper" shortcodes
650 * with the format [shortcode=http://url.com].
651 *
652 * This removes the "=" from the shortcode so it can be parsed.
653 *
654 * @see https://github.com/Automattic/jetpack/issues/3121
655 *
656 * @param string $content HTML content.
657 */
658 function jetpack_fix_youtube_shortcode_display_filter( $content ) {
659 if ( strpos( $content, '[youtube=' ) !== false ) {
660 $content = preg_replace( '@\[youtube=(.*?)\]@', '[youtube $1]', $content );
661 }
662
663 return $content;
664 }
665 add_filter( 'the_content', 'jetpack_fix_youtube_shortcode_display_filter', 7 );
666