PluginProbe
FV Player 8 / 8.0.21
FV Player 8 v8.0.21
trunk 8.0.18 8.0.19 8.0.20 8.0.21 8.0.25 8.0.27 8.1 8.1.3
fv-player / controller / frontend.php

frontend.php in FV Player 8 8.0.21, at controller/frontend.php

1,321 lines 50.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /* FV Player - HTML5 video player
3 Copyright (C) 2013 Foliovision
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 add_action('wp_footer','flowplayer_prepare_scripts',9);
24 add_action('wp_footer','flowplayer_display_scripts',100);
25 add_action('widget_text','do_shortcode');
26
27 add_filter( 'run_ngg_resource_manager', '__return_false' );
28
29
30 function fv_flowplayer_remove_bad_scripts() {
31 global $wp_scripts;
32 if( isset($wp_scripts->registered['flowplayer']) && isset($wp_scripts->registered['flowplayer']->src) && stripos($wp_scripts->registered['flowplayer']->src, 'fv-player') === false ) {
33 wp_deregister_script( 'flowplayer' );
34 }
35 }
36 add_action( 'wp_print_scripts', 'fv_flowplayer_remove_bad_scripts', 100 );
37
38 add_filter( 'run_ngg_resource_manager', '__return_false' ); // Nextgen Gallery compatibility fix
39
40 function fv_flowplayer_ap_action_init(){
41 // Localization
42 load_plugin_textdomain('fv-player', false, dirname(dirname(plugin_basename(__FILE__))) . "/languages");
43 }
44 add_action('init', 'fv_flowplayer_ap_action_init');
45
46 function fv_flowplayer_get_js_translations() {
47
48 $aStrings = array(
49 0 => '',
50 1 => __('Video loading aborted', 'fv-player' ),
51 2 => __('Network error', 'fv-player' ),
52 3 => __('Video not properly encoded', 'fv-player' ),
53 4 => __('Video file not found', 'fv-player' ),
54 5 => __('Unsupported video', 'fv-player' ),
55 6 => __('Skin not found', 'fv-player' ),
56 7 => __('SWF file not found', 'fv-player' ),
57 8 => __('Subtitles not found', 'fv-player' ),
58 10 => __('Unsupported video format.', 'fv-player' ),
59 11 => __('Click to watch the video', 'fv-player' ),
60 12 => __('[This post contains video, click to play]', 'fv-player' ),
61 'video_expired' => __('<h2>Video file expired.<br />Please reload the page and play it again.</h2>', 'fv-player' ),
62 'unsupported_format' => __('<h2>Unsupported video format.<br />Please use a Flash compatible device.</h2>', 'fv-player' ),
63 'mobile_browser_detected_1' => __('Mobile browser detected, serving low bandwidth video.', 'fv-player' ),
64 'mobile_browser_detected_2' => __('Click here for full quality', 'fv-player' ),
65 'live_stream_failed' => __('<h2>Live stream load failed.</h2><h3>Please try again later, perhaps the stream is currently offline.</h3>', 'fv-player' ),
66 'live_stream_failed_2' => __('<h2>Live stream load failed.</h2><h3>Please try again later, perhaps the stream is currently offline.</h3>', 'fv-player' ),
67 'what_is_wrong' => __('Please tell us what is wrong :', 'fv-player' ),
68 'full_sentence' => __('Please give us more information (a full sentence) so we can help you better', 'fv-player' ),
69 'error_JSON' =>__('Admin: Error parsing JSON', 'fv-player' ),
70 'no_support_IE9' =>__('Admin: Video checker doesn\'t support IE 9.', 'fv-player' ),
71 'check_failed' =>__('Admin: Check failed.', 'fv-player' ),
72 'playlist_current' =>__('Now Playing', 'fv-player' ),
73 'playlist_item_no' =>__('Item %d.', 'fv-player' ),
74 'playlist_play_all' =>__('Play All', 'fv-player' ),
75 'playlist_play_all_button' =>__('All', 'fv-player' ),
76 'playlist_replay_all' =>__('Replay Playlist', 'fv-player' ),
77 'playlist_replay_video' =>__('Repeat Track', 'fv-player' ),
78 'playlist_shuffle' =>__('Shuffle Playlist', 'fv-player' ),
79 'video_issues' =>__('Video Issues', 'fv-player' ),
80 'video_reload' =>__('Video loading has stalled, click to reload', 'fv-player' ),
81 'link_copied' =>__('Video Link Copied to Clipboard', 'fv-player' ),
82 'live_stream_starting'=>__('<h2>Live stream scheduled</h2><p>Starting in <span>%d</span>.</p>', 'fv-player' ),
83 'live_stream_retry'=>__( '<h2>We are sorry, currently no live stream available.</h2><p>Retrying in <span>%d</span> ...</p>', 'fv-player' ),
84 'live_stream_continue'=>__( '<h2>It appears the stream went down.</h2><p>Retrying in <span>%d</span> ...</p>', 'fv-player' ),
85 'embed_copied' =>__('Embed Code Copied to Clipboard', 'fv-player' ),
86 'error_copy_clipboard' => __('Error copying text into clipboard!', 'fv-player' ),
87 'subtitles_disabled' =>__('Subtitles disabled', 'fv-player' ),
88 'subtitles_switched' =>__('Subtitles switched to ', 'fv-player' ),
89 'warning_iphone_subs' => __('This video has subtitles, that are not supported on your device.', 'fv-player' ),
90 'warning_unstable_android' => __('You are using an old Android device. If you experience issues with the video please use <a href="https://play.google.com/store/apps/details?id=org.mozilla.firefox">Firefox</a>.', 'fv-player' ),
91 'warning_samsungbrowser' => __('You are using the Samsung Browser which is an older and buggy version of Google Chrome. If you experience issues with the video please use <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> or other modern browser.', 'fv-player' ),
92 'warning_old_safari' => __('You are using an old Safari browser. If you experience issues with the video please use <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> or other modern browser.', 'fv-player' ),
93 'warning_old_chrome' => __('You are using an old Chrome browser. Please make sure you use the latest version.', 'fv-player' ),
94 'warning_old_firefox' => __('You are using an old Firefox browser. Please make sure you use the latest version.', 'fv-player' ),
95 'warning_old_ie' => __('You are using a deprecated browser. If you experience issues with the video please use <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> or other modern browser.', 'fv-player' ),
96 'quality' => __('Quality', 'fv-player' ),
97 'closed_captions' => __('Closed Captions', 'fv-player' ),
98 'no_subtitles' => __('No subtitles', 'fv-player' ),
99 'speed' => __('Speed', 'fv-player' ),
100 'duration_1_day' => __( "%s day" ),
101 'duration_n_days' => _n( '%s day', '%s days', 5 ),
102 'duration_1_hour' => __( "%s hour" ),
103 'duration_n_hours' => _n( '%s hour', '%s hours', 5 ),
104 'duration_1_minute' => __( "%s min" ),
105 'duration_n_minutes' => _n( '%s min', '%s mins', 5 ),
106 'duration_1_second' => __( "%s second" ),
107 'duration_n_seconds' => _n( '%s second', '%s seconds', 5 ),
108 'and' => sprintf( __( '%1$s and %2$s' ), '', '' ),
109 'chrome_extension_disable_html5_autoplay' => __('It appears you are using the Disable HTML5 Autoplay Chrome extension, disable it to play videos', 'fv-player' ),
110 'click_to_unmute' => __('Click to unmute', 'fv-player' ),
111 'audio_button' => __('AUD', 'fv-player' ),
112 'audio_menu' => __('Audio', 'fv-player' ),
113 'iphone_swipe_up_location_bar' => __('To enjoy fullscreen swipe up to hide location bar.', 'fv-player' ),
114 'invalid_youtube' => __('Invalid Youtube video ID.', 'fv-player'),
115 'redirection' => __( "Admin note:\n\nThis player is set to redirect to a URL at the end of the video:\n\n%url%\n\nWould you like to be redirected?\n\nThis note only shows to logged in Administrators and Editors for security reasons, other users are redirected without any popup or confirmation.", 'fv-player' ),
116 'video_loaded' => __('Video loaded, click to play.', 'fv-player'),
117 'msg_no_skipping' => __('Skipping is not allowed.', 'fv-player' ),
118 'msg_watch_video' => __('Please watch the video carefully.', 'fv-player' ),
119 );
120
121 return $aStrings;
122 }
123
124 /**
125 * Replaces the flowplayer tags in post content by players and fills the $GLOBALS['fv_fp_scripts'] array.
126 *
127 * @param string Content to be parsed
128 * @return string Modified content string
129 */
130 function flowplayer_content( $content ) {
131 global $fv_fp;
132
133 $content_matches = array();
134 preg_match_all('/\[(flowplayer|fvplayer)\ [^\]]+\]/i', $content, $content_matches);
135
136 // process all found tags
137 foreach ($content_matches[0] as $tag) {
138 $ntag = str_replace("\'",'&#039;',$tag);
139 //search for URL
140 preg_match("/src='([^']*?)'/i",$ntag,$tmp);
141 if( $tmp[1] == NULL ) {
142 preg_match_all("/src=([^,\s\]]*)/i",$ntag,$tmp);
143 $media = $tmp[1][0];
144 }
145 else
146 $media = $tmp[1];
147
148 //strip the additional /videos/ from the beginning if present
149 preg_match('/(.*)\/videos\/(.*)/',$media,$matches);
150 if ($matches[0] == NULL)
151 $media = $media;
152 else if ($matches[1] == NULL) {
153 $media = $matches[2];
154 }
155 else {
156 $media = $matches[2];
157 }
158
159 unset($arguments['src']);
160 unset($arguments['src1']);
161 unset($arguments['src2']);
162 unset($arguments['width']);
163 unset($arguments['height']);
164 unset($arguments['autoplay']);
165 unset($arguments['splash']);
166 unset($arguments['splashend']);
167 unset($arguments['popup']);
168 unset($arguments['controlbar']);
169 unset($arguments['redirect']);
170 unset($arguments['loop']);
171
172 //width and heigth
173 preg_match("/width=(\d*)/i",$ntag,$width);
174 preg_match("/height=(\d*)/i",$ntag,$height);
175 if( $width[1] != NULL)
176 $arguments['width'] = $width[1];
177 if( $height[1] != NULL)
178 $arguments['height'] = $height[1];
179
180 //search for redirect
181 preg_match("/redirect='([^']*?)'/i",$ntag,$tmp);
182 if ($tmp[1])
183 $arguments['redirect'] = $tmp[1];
184
185 //search for autoplay
186 preg_match("/[\s]+autoplay([\s]|])+/i",$ntag,$tmp);
187 if (isset($tmp[0])){
188 $arguments['autoplay'] = true;
189 }
190 else {
191 preg_match("/autoplay='([A-Za-z]*)'/i",$ntag,$tmp);
192 if ( $tmp[1] == NULL )
193 preg_match("/autoplay=([A-Za-z]*)/i",$ntag,$tmp);
194 if (isset($tmp[1]))
195 $arguments['autoplay'] = $tmp[1];
196 }
197
198 //search for popup in quotes
199 preg_match("/popup='([^']*?)'/i",$ntag,$tmp);
200 if ($tmp[1])
201 $arguments['popup'] = $tmp[1];
202
203 //search for loop
204 preg_match("/[\s]+loop([\s]|])+/i",$ntag,$tmp);
205 if (isset($tmp[0])){
206 $arguments['loop'] = true;
207 }
208 else {
209 preg_match("/loop='([A-Za-z]*)'/i",$ntag,$tmp);
210 if ( $tmp[1] == NULL )
211 preg_match("/loop=([A-Za-z]*)/i",$ntag,$tmp);
212 if (isset($tmp[1]))
213 $arguments['loop'] = $tmp[1];
214 }
215
216 // search for splash image
217 preg_match("/splash='([^']*?)'/i",$ntag,$tmp); //quotes version
218 if( $tmp[1] == NULL ) {
219 preg_match_all("/splash=([^,\s\]]*)/i",$ntag,$tmp); //non quotes version
220 preg_match('/(.*)\/videos\/(.*)/i',$tmp[1][0],$matches);
221 if ($matches[0] == NULL)
222 $arguments['splash'] = $tmp[1][0];
223 else if ($matches[1] == NULL) {
224 $arguments['splash'] = $matches[2];//$tmp[1][0];
225 }
226 else {
227 $arguments['splash'] = $matches[2];
228 }
229 }
230 else {
231 preg_match('/(.*)\/videos\/(.*)/',$tmp[1],$matches);
232 if ($matches[0] == NULL)
233 $arguments['splash'] = $tmp[1];
234 elseif ($matches[1] == NULL)
235 $arguments['splash'] = $matches[2];
236 else
237 $arguments['splash'] = $matches[2];//$tmp[1];
238 }
239
240 // search for src1
241 preg_match("/src1='([^']*?)'/i",$ntag,$tmp); //quotes version
242 if( $tmp[1] == NULL ) {
243 preg_match_all("/src1=([^,\s\]]*)/i",$ntag,$tmp); //non quotes version
244 preg_match('/(.*)\/videos\/(.*)/i',$tmp[1][0],$matches);
245 if ($matches[0] == NULL)
246 $arguments['src1'] = $tmp[1][0];
247 else if ($matches[1] == NULL) {
248 $arguments['src1'] = $matches[2];//$tmp[1][0];
249 }
250 else {
251 $arguments['src1'] = $matches[2];
252 }
253 }
254 else {
255 preg_match('/(.*)\/videos\/(.*)/',$tmp[1],$matches);
256 if ($matches[0] == NULL)
257 $arguments['src1'] = $tmp[1];
258 elseif ($matches[1] == NULL)
259 $arguments['src1'] = $matches[2];
260 else
261 $arguments['src1'] = $matches[2];//$tmp[1];
262 }
263
264 // search for src1
265 preg_match("/src2='([^']*?)'/i",$ntag,$tmp); //quotes version
266 if( $tmp[1] == NULL ) {
267 preg_match_all("/src2=([^,\s\]]*)/i",$ntag,$tmp); //non quotes version
268 preg_match('/(.*)\/videos\/(.*)/i',$tmp[1][0],$matches);
269 if ($matches[0] == NULL)
270 $arguments['src2'] = $tmp[1][0];
271 else if ($matches[1] == NULL) {
272 $arguments['src2'] = $matches[2];//$tmp[1][0];
273 }
274 else {
275 $arguments['src2'] = $matches[2];
276 }
277 }
278 else {
279 preg_match('/(.*)\/videos\/(.*)/',$tmp[1],$matches);
280 if ($matches[0] == NULL)
281 $arguments['src2'] = $tmp[1];
282 elseif ($matches[1] == NULL)
283 $arguments['src2'] = $matches[2];
284 else
285 $arguments['src2'] = $matches[2];//$tmp[1];
286 }
287
288 //search for splashend
289 preg_match("/[\s]+splashend([\s]|])+/i",$ntag,$tmp);
290 if (isset($tmp[0])){
291 $arguments['splashend'] = true;
292 }
293 else {
294 preg_match("/splashend='([A-Za-z]*)'/i",$ntag,$tmp);
295 if ( $tmp[1] == NULL )
296 preg_match("/splashend=([A-Za-z]*)/i",$ntag,$tmp);
297 if (isset($tmp[1]))
298 $arguments['splashend'] = $tmp[1];
299 }
300
301 //search for controlbar
302 preg_match("/[\s]+controlbar([\s]|])+/i",$ntag,$tmp);
303 if (isset($tmp[0])){
304 $arguments['controlbar'] = true;
305 }
306 else {
307 preg_match("/controlbar='([A-Za-z]*)'/i",$ntag,$tmp);
308 if ( $tmp[1] == NULL )
309 preg_match("/controlbar=([A-Za-z]*)/i",$ntag,$tmp);
310 if (isset($tmp[1]))
311 $arguments['controlbar'] = $tmp[1];
312 }
313
314 if (trim($media) != '') {
315 // build new player
316 $new_player = $fv_fp->build_min_player($media,$arguments);
317 $content = str_replace($tag, $new_player['html'],$content);
318 if (!empty($new_player['script'])) {
319 $GLOBALS['fv_fp_scripts'] = $new_player['script'];
320 }
321 }
322 }
323 return $content;
324 }
325
326 function flowplayer_prepare_scripts() {
327 global $fv_fp, $fv_wp_flowplayer_ver, $fv_wp_flowplayer_core_ver;
328
329 // don't load script in Optimize Press 2 preview
330 if( flowplayer::is_special_editor() ) {
331 return;
332 }
333
334 if(
335 isset($GLOBALS['fv_fp_scripts']) ||
336 $fv_fp->should_force_load_js() ||
337 isset( $_GET['fv_wp_flowplayer_check_template'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['fv_wp_flowplayer_check_template'] ) ), 'fv_wp_flowplayer_check_template' )
338 ){
339
340 $aDependencies = array('jquery');
341 if( $fv_fp->should_force_load_js() || $fv_fp->load_tabs ) {
342 wp_enqueue_script('jquery-ui-tabs', false, array('jquery','jquery-ui-core'), $fv_wp_flowplayer_ver, true);
343 $aDependencies[] = 'jquery-ui-tabs';
344 }
345
346 if( !$fv_fp->bCSSLoaded ) $fv_fp->css_enqueue(true);
347
348 $sLogo = $fv_fp->_get_option('logo') ? $fv_fp->_get_option('logo') : '';
349
350 // Load base Freedom Video Player library
351 $path = '/freedom-video-player/freedomplayer.min.js';
352 if( file_exists(dirname(__FILE__).'/../freedom-video-player/freedomplayer.js') ) {
353 $path = '/freedom-video-player/freedomplayer.js';
354 }
355
356 $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? filemtime( dirname(__FILE__).'/../'.$path ) : $fv_wp_flowplayer_core_ver;
357
358 wp_enqueue_script( 'flowplayer', flowplayer::get_plugin_url().$path, $aDependencies, $version, true );
359 $aDependencies[] = 'flowplayer';
360
361 // Load modules
362 if( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) {
363 $path = '/freedom-video-player/modules/fv-player.js';
364 wp_enqueue_script( 'fv-player', flowplayer::get_plugin_url().$path, $aDependencies, filemtime( dirname(__FILE__).'/../'.$path ), true );
365 $aDependencies[] = 'fv-player';
366
367 foreach( glob( dirname(dirname(__FILE__)).'/freedom-video-player/modules/*.module.js') as $filename ) {
368 $path = '/freedom-video-player/modules/'.basename($filename);
369 wp_enqueue_script( 'fv-player-'.basename($filename), flowplayer::get_plugin_url().$path, $aDependencies, filemtime( dirname(__FILE__).'/../'.$path ), true);
370 }
371
372 } else {
373 wp_enqueue_script( 'fv-player', flowplayer::get_plugin_url().'/freedom-video-player/fv-player.min.js', $aDependencies, $fv_wp_flowplayer_ver, true );
374
375 }
376
377 if( current_user_can('manage_options') && !$fv_fp->_get_option('disable_videochecker') && ( $fv_fp->_get_option('video_checker_agreement') || $fv_fp->_get_option('key_automatic') ) ) {
378 wp_enqueue_script( 'fv-player-video-checker', flowplayer::get_plugin_url().'/js/video-checker.js', array('flowplayer'), $fv_wp_flowplayer_ver, true );
379 $aConf['video_checker'] = true;
380 }
381
382 if( $fv_fp->_get_option('ui_speed_increment') == 0.25){
383 $aConf['speeds'] = array( 0.25,0.5,0.75,1,1.25,1.5,1.75,2 );
384 }elseif( $fv_fp->_get_option('ui_speed_increment') == 0.1){
385 $aConf['speeds'] = array( 0.25,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2 );
386 }elseif( $fv_fp->_get_option('ui_speed_increment') == 0.5){
387 $aConf['speeds'] = array( 0.5,1,1.5,2 );
388 }
389
390 $aConf['video_hash_links'] = empty($fv_fp->aCurArgs['linking']) ? $fv_fp->_get_option( 'ui_video_links' ) : $fv_fp->aCurArgs['linking'] === 'true';
391
392 if( apply_filters( 'fv_flowplayer_safety_resize', true) ) {
393 $aConf['safety_resize'] = true;
394 }
395 if( $fv_fp->_get_option('cbox_compatibility') ) {
396 $aConf['cbox_compatibility'] = true;
397 }
398 if( current_user_can('manage_options') && !$fv_fp->_get_option('disable_videochecker') ) {
399 $aConf['video_checker_site'] = home_url();
400 }
401
402 if( $sLogo ) $aConf['logo'] = $sLogo;
403 if ( $fv_fp->_get_option( 'logo_over_video' ) ) $aConf['logo_over_video'] = true;
404
405 // Used to restore volume, removed in JS if volume stored in browser localStorage
406 $aConf['volume'] = floatval( $fv_fp->_get_option('volume') );
407 if( $aConf['volume'] > 1 ) {
408 $aConf['volume'] = 1;
409 }
410
411 $aConf['default_volume'] = $aConf['volume'];
412
413 if( $val = $fv_fp->_get_option('mobile_native_fullscreen') ) $aConf['mobile_native_fullscreen'] = $val;
414 if( $val = $fv_fp->_get_option('mobile_force_fullscreen') ) $aConf['mobile_force_fullscreen'] = $val;
415 if( $val = $fv_fp->_get_option('mobile_alternative_fullscreen') ) $aConf['mobile_alternative_fullscreen'] = $val;
416 $aConf['mobile_landscape_fullscreen'] = true;
417
418 if ( $fv_fp->_get_option('video_position_save_enable') ) {
419 $aConf['video_position_save_enable'] = $fv_fp->_get_option('video_position_save_enable');
420 }
421
422 if( is_user_logged_in() ) $aConf['is_logged_in'] = true;
423
424 if ( current_user_can( 'edit_posts' ) ) {
425 $aConf['is_logged_in_editor'] = true;
426 }
427
428 $aConf['sticky_video'] = $fv_fp->_get_option('sticky_video');
429 $aConf['sticky_place'] = $fv_fp->_get_option('sticky_place');
430 $aConf['sticky_min_width'] = intval( apply_filters( 'fv_player_sticky_desktop_min_width', 1020 ) );
431
432 global $post;
433 if( $post && isset($post->ID) && $post->ID > 0 ) {
434 if( get_post_meta($post->ID, 'fv_player_mobile_native_fullscreen', true) ) $aConf['mobile_native_fullscreen'] = true;
435 if( get_post_meta($post->ID, 'fv_player_mobile_force_fullscreen', true) ) $aConf['mobile_force_fullscreen'] = true;
436 }
437
438 if( $fv_fp->should_force_load_js() || $fv_fp->load_hlsjs ) {
439 wp_enqueue_script( 'flowplayer-hlsjs', flowplayer::get_plugin_url().'/freedom-video-player/hls.min.js', array('flowplayer'), '1.6.5', true );
440 }
441 $aConf['script_hls_js'] = flowplayer::get_plugin_url().'/freedom-video-player/hls.min.js?ver=1.6.5';
442
443 $dashjs_version = '3.2.2-mod';
444
445 $fv_player_dashjs = 'fv-player-dashjs.min.js';
446 if( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) $fv_player_dashjs = 'fv-player-dashjs.dev.js';
447
448 if( $fv_fp->should_force_load_js() || $fv_fp->load_dash ) {
449 wp_enqueue_script( 'dashjs', flowplayer::get_plugin_url().'/freedom-video-player/dash.mediaplayer.min.js', array('flowplayer'), $dashjs_version, true );
450 wp_enqueue_script( 'fv-player-dash', flowplayer::get_plugin_url().'/freedom-video-player/'.$fv_player_dashjs, array('dashjs'), $fv_wp_flowplayer_ver, true );
451 }
452
453 // Used by FV Player Pro in case Dash.js was not loaded for the page
454 $aConf['script_dash_js'] = flowplayer::get_plugin_url().'/freedom-video-player/dash.mediaplayer.min.js?ver='.$dashjs_version;
455 $aConf['script_dash_js_engine'] = flowplayer::get_plugin_url().'/freedom-video-player/'.$fv_player_dashjs.'?ver='.$fv_wp_flowplayer_ver;
456
457 if( $fv_fp->should_force_load_js() || FV_Player_YouTube()->bYoutube || did_action('fv_player_extensions_admin_load_assets') ) {
458 $youtube_js = 'fv-player-youtube.min.js';
459 $youtube_ver = $fv_wp_flowplayer_ver;
460
461 if( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) {
462 $youtube_js = 'fv-player-youtube.dev.js';
463 $youtube_ver = filemtime( dirname(__FILE__).'/../freedom-video-player/'.$youtube_js );
464 }
465
466 wp_enqueue_script( 'fv-player-youtube', flowplayer::get_plugin_url().'/freedom-video-player/' . $youtube_js , array('flowplayer'), $youtube_ver, true );
467 }
468
469 if( $fv_fp->_get_option('googleanalytics') ) {
470 $aConf['fvanalytics'] = $fv_fp->_get_option('googleanalytics');
471 }
472
473 if( $fv_fp->_get_option('matomo_domain') && $fv_fp->_get_option('matomo_site_id') ) {
474 // take the domain name from Matomo Domain setting in case somebody entered full URL
475 $matomo_domain = $fv_fp->_get_option('matomo_domain');
476 $parsed = wp_parse_url($matomo_domain);
477 if( $parsed && !empty($parsed['host']) ) {
478 $matomo_domain = $parsed['host'];
479 if( !empty($parsed['path']) ) {
480 $matomo_domain .= '/'.$parsed['path'];
481 }
482 }
483 $aConf['matomo_domain'] = $matomo_domain;
484 $aConf['matomo_site_id'] = $fv_fp->_get_option('matomo_site_id');
485 }
486
487 if( $fv_fp->_get_option('ui_airplay') ) {
488 $aConf['airplay'] = true;
489 }
490
491 $aConf['chromecast'] = false; // tell core Freedom Video Player and FV Player Pro <= 7.4.43.727 to not load Chromecast
492 if( $fv_fp->_get_option('chromecast') ) {
493 $aConf['chromecast'] = array(
494 'applicationId' => '908E271B'
495 );
496 }
497
498 if( $fv_fp->_get_option('hd_streaming') ) {
499 $aConf['hd_streaming'] = true;
500 }
501
502 if( $fv_fp->_get_option('multiple_playback') ) {
503 $aConf['multiple_playback'] = true;
504 }
505
506 if( $fv_fp->_get_option('disable_localstorage') ) {
507 $aConf['disable_localstorage'] = true;
508 }
509
510 if( $fv_fp->_get_option('autoplay_preload') && $fv_fp->_get_option('autoplay_preload') != 'false' ) {
511 $aConf['autoplay_preload'] = $fv_fp->_get_option('autoplay_preload');
512 }
513
514 if( $fv_fp->_get_option('youtube_browser_chrome') ) {
515 $aConf['youtube_browser_chrome'] = $fv_fp->_get_option('youtube_browser_chrome');
516 }
517
518 $aConf['hlsjs'] = array(
519 'startLevel' => -1,
520 'fragLoadingMaxRetry' => 3,
521 'levelLoadingMaxRetry' => 3,
522 'capLevelToPlayerSize' => true,
523 'use_for_safari' => class_exists( 'FV_Player_DRM' ),
524 );
525
526 // The above HLS.js config doesn't work well on Chrome and Firefox, so we detect that in JS and use this config for it instead. Todo: make this a per-video thing
527 if( class_exists('FV_Player_Pro_DaCast') ) {
528 $aConf['dacast_hlsjs'] = array(
529 'autoLevelEnabled' => false // disable ABR. If you set startLevel or capLevelToPlayerSize it will be enabled again. So this way everybody on desktop gets top quality and they have to switch to lower each time.
530 );
531 }
532
533 if( is_admin() ) $aConf['wpadmin'] = true;
534
535 // Is it the wp-admin -> FV Player -> Settings screen?
536 if ( did_action( 'admin_head-fv-player_page_fvplayer' ) ) {
537 $aConf['skin_preview'] = true;
538 }
539
540 $aConf = apply_filters( 'fv_flowplayer_conf', $aConf );
541
542 $aLocalize = array(
543 'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php',
544 'nonce' => wp_create_nonce( 'fv_player_frontend' ),
545 'email_signup_nonce' => wp_create_nonce( 'fv_player_email_signup' ),
546 'video_position_save_nonce' => wp_create_nonce( 'fv_player_video_position_save' ),
547 );
548
549 wp_localize_script( 'flowplayer', 'fv_flowplayer_conf', $aConf );
550 if( current_user_can('manage_options') ) {
551 $aLocalize['admin_input'] = true;
552 $aLocalize['admin_js_test'] = true;
553 }
554
555 if( current_user_can('edit_posts') ) {
556 $aLocalize['user_edit'] = true;
557 }
558
559 wp_localize_script( 'flowplayer', 'fv_player', $aLocalize );
560
561 wp_localize_script( 'flowplayer', 'fv_flowplayer_translations', fv_flowplayer_get_js_translations());
562 wp_localize_script( 'flowplayer', 'fv_flowplayer_playlists', array() ); // has to be defined for FV Player Pro 0.6.20 and such
563
564 if( isset($GLOBALS['fv_fp_scripts']) && count($GLOBALS['fv_fp_scripts']) > 0 ) {
565 foreach( $GLOBALS['fv_fp_scripts'] AS $sKey => $aScripts ) {
566 wp_localize_script( 'flowplayer', $sKey.'_array', $aScripts );
567 }
568 }
569
570 }
571
572 FV_Player_lightbox()->maybe_load();
573 }
574
575 /**
576 * Prints flowplayer javascript content to the bottom of the page.
577 */
578 function flowplayer_display_scripts() {
579 if( flowplayer::is_special_editor() ) {
580 return;
581 }
582
583 if( is_user_logged_in() || isset($_GET['fv_wp_flowplayer_check_template']) ) {
584 echo "\n<!--fv-flowplayer-footer-->\n\n";
585 }
586 }
587
588 /**
589 * This is the template tag. Use the standard Flowplayer shortcodes
590 */
591 function flowplayer($shortcode) {
592 echo apply_filters('the_content',$shortcode);
593 }
594
595
596 /*
597 Make sure our div won't be wrapped in any P tag and that html attributes don't break the shortcode
598 */
599 function fv_flowplayer_the_content( $c ) {
600 if( flowplayer::is_special_editor() ) {
601 return $c;
602 }
603
604 $c = preg_replace( '!<p[^>]*?>(\[(?:fvplayer|flowplayer).*?[^\\\]\])</p>!', "\n".'$1'."\n", $c );
605 $c = preg_replace_callback( '!\[(?:fvplayer|flowplayer).*?[^\\\]\]!', 'fv_flowplayer_shortfcode_fix_attrs', $c );
606 return $c;
607 }
608 add_filter( 'the_content', 'fv_flowplayer_the_content', 0 );
609
610
611 function fv_flowplayer_shortfcode_fix_attrs( $aMatch ) {
612 $aMatch[0] = preg_replace_callback( '!(?:ad|popup)="(.*?[^\\\])"!', 'fv_flowplayer_shortfcode_fix_attr', $aMatch[0] );
613 return $aMatch[0];
614 }
615
616
617 function fv_flowplayer_shortfcode_fix_attr( $aMatch ) {
618 $aMatch[0] = str_replace( $aMatch[1], '<!--fv_flowplayer_base64_encoded-->'.base64_encode($aMatch[1]), $aMatch[0] );
619 return $aMatch[0];
620 }
621
622
623 /*
624 Handle attachment pages which contain videos
625 */
626 function fv_flowplayer_attachment_page_video( $c ) {
627 global $post;
628 if( stripos($post->post_mime_type, 'video/') !== 0 && stripos($post->post_mime_type, 'audio/') !== 0 ) {
629 return $c;
630 }
631
632 if( !$src = wp_get_attachment_url($post->ID) ) {
633 return $c;
634 }
635
636 $meta = get_post_meta( $post->ID, '_wp_attachment_metadata', true );
637 $size = (isset($meta['width']) && isset($meta['height']) && intval($meta['width'])>0 && intval($meta['height'])>0 ) ? ' width="'.intval($meta['width']).'" height="'.intval($meta['height']).'"' : false;
638
639 $shortcode = '[fvplayer src="'.$src.'"'.$size.']';
640
641 $c = preg_replace( '~<p class=.attachment.[\s\S]*?</p>~', $shortcode, $c );
642 $c = preg_replace( '~<div[^>]*?class="[^"]*?wp-video[^"]*?"[^>]*?>[\s\S]*?<video.*?</video></div>~', $shortcode, $c );
643
644 return $c;
645 }
646 add_filter( 'prepend_attachment', 'fv_flowplayer_attachment_page_video' );
647
648
649 function fv_player_title( $title ) {
650 global $post, $authordata;
651 $sAuthorInfo = ( $authordata ) ? sprintf( '<a href="%1$s" title="%2$s" rel="author">%3$s</a>', esc_url( get_author_posts_url( $authordata->ID, $authordata->user_nicename ) ), esc_attr( sprintf( __( 'Posts by %s' ), get_the_author() ) ), get_the_author() ) : false;
652 $title = str_replace(
653 array(
654 '%post_title%',
655 '%post_date%',
656 '%post_author%',
657 '%post_author_name%'
658 ),
659 array(
660 get_the_title(),
661 get_the_date(),
662 $sAuthorInfo,
663 get_the_author()
664 ),
665 $title );
666 return $title;
667 }
668 add_filter( 'fv_player_title', 'fv_player_title' );
669
670
671 add_filter( 'comment_text', 'fv_player_comment_text', 0 );
672 add_filter( 'bp_get_activity_content_body', 'fv_player_comment_text', 6 );
673 add_filter( 'bbp_get_topic_content', 'fv_player_comment_text', 0 );
674 add_filter( 'bbp_get_reply_content', 'fv_player_comment_text', 0 );
675
676 function fv_player_comment_text( $comment_text ) {
677 if( is_admin() ) return $comment_text;
678
679 global $fv_fp;
680 if( isset($fv_fp->conf['parse_comments']) && $fv_fp->conf['parse_comments'] == 'true' ) {
681 add_filter('comment_text', 'do_shortcode');
682 add_filter('bbp_get_topic_content', 'do_shortcode', 11);
683 add_filter('bbp_get_reply_content', 'do_shortcode', 11);
684
685 if( stripos($comment_text,'youtube.com') !== false || stripos($comment_text,'youtu.be') !== false ) {
686 $pattern = '#(?:<iframe[^>]*?src=[\'"])?((?:https?://|//)?' # Optional URL scheme. Either http, or https, or protocol-relative.
687 . '(?:www\.|m\.)?' # Optional www or m subdomain.
688 . '(?:' # Group host alternatives:
689 . 'youtu\.be/' # Either youtu.be,
690 . '|youtube\.com/' # or youtube.com
691 . '(?:' # Group path alternatives:
692 . 'embed/' # Either /embed/,
693 . '|v/' # or /v/,
694 . '|watch\?v=' # or /watch?v=,
695 . '|watch\?.+&v=' # or /watch?other_param&v=
696 . ')' # End path alternatives.
697 . ')' # End host alternatives.
698 . '([\w-]{11})' # 11 characters (Length of Youtube video ids).
699 . '(?![\w-]))(?:.*?</iframe>)?#'; # Rejects if overlong id.
700 $comment_text = preg_replace( $pattern, '[fvplayer src="$1"]', $comment_text );
701 }
702
703 if( stripos($comment_text,'vimeo.com') !== false ) {
704 $pattern = '#(?:https?://)?(?:www.)?(?:player.)?vimeo.com/(?:[/a-z]*/)*([0-9]{6,11})[?]?.*#';
705 $comment_text = preg_replace( $pattern, '[fvplayer src="https://vimeo.com/$1"]', $comment_text );
706 }
707 }
708
709 return $comment_text;
710 }
711
712 add_action( 'fv_player_extensions_admin_load_assets', 'fv_player_footer_svg_playlist' );
713
714 function fv_player_footer_svg_playlist() {
715 if( file_exists(dirname( __FILE__ ) . '/../css/fvp-icon-sprite.svg') ) {
716 include_once(dirname( __FILE__ ) . '/../css/fvp-icon-sprite.svg');
717 }
718 }
719
720 add_action( 'fv_player_extensions_admin_load_assets', 'fv_player_footer_svg_rewind' );
721
722 function fv_player_footer_svg_rewind() {
723 ?>
724 <svg style="position: absolute; width: 0; height: 0; overflow: hidden;" class="fvp-icon" xmlns="https://www.w3.org/2000/svg">
725 <g id="fvp-rewind">
726 <path d="M22.7 10.9c0 1.7-0.4 3.3-1.1 4.8 -0.7 1.5-1.8 2.8-3.2 3.8 -0.4 0.3-1.3-0.9-0.9-1.2 1.2-0.9 2.1-2 2.7-3.3 0.7-1.3 1-2.7 1-4.1 0-2.6-0.9-4.7-2.7-6.5 -1.8-1.8-4-2.7-6.5-2.7 -2.5 0-4.7 0.9-6.5 2.7 -1.8 1.8-2.7 4-2.7 6.5 0 2.4 0.8 4.5 2.5 6.3 1.7 1.8 3.7 2.7 6.1 2.9l-1.2-2c-0.2-0.3 0.9-1 1.1-0.7l2.3 3.7c0.2 0.3 0 0.6-0.2 0.7L9.5 23.8c-0.3 0.2-0.9-0.9-0.5-1.2l2.1-1.1c-2.7-0.2-5-1.4-6.9-3.4 -1.9-2-2.8-4.5-2.8-7.2 0-3 1.1-5.5 3.1-7.6C6.5 1.2 9 0.2 12 0.2c3 0 5.5 1.1 7.6 3.1C21.7 5.4 22.7 7.9 22.7 10.9z" /><path d="M8.1 15.1c-0.1 0-0.1 0-0.1-0.1V8C8 7.7 7.8 7.9 7.7 7.9L6.8 8.3C6.8 8.4 6.7 8.3 6.7 8.2L6.3 7.3C6.2 7.2 6.3 7.1 6.4 7.1l2.7-1.2c0.1 0 0.4 0 0.4 0.3v8.8c0 0.1 0 0.1-0.1 0.1H8.1z" /><path d="M17.7 10.6c0 2.9-1.3 4.7-3.5 4.7 -2.2 0-3.5-1.8-3.5-4.7s1.3-4.7 3.5-4.7C16.4 5.9 17.7 7.7 17.7 10.6zM12.3 10.6c0 2.1 0.7 3.4 2 3.4 1.3 0 2-1.2 2-3.4 0-2.1-0.7-3.4-2-3.4C13 7.2 12.3 8.5 12.3 10.6z" />
727 </g>
728 </svg>
729 <svg style="position: absolute; width: 0; height: 0; overflow: hidden;" class="fvp-icon" xmlns="https://www.w3.org/2000/svg">
730 <g id="fvp-forward">
731 <path d="M22.7 10.9c0 1.7-0.4 3.3-1.1 4.8 -0.7 1.5-1.8 2.8-3.2 3.8 -0.4 0.3-1.3-0.9-0.9-1.2 1.2-0.9 2.1-2 2.7-3.3 0.7-1.3 1-2.7 1-4.1 0-2.6-0.9-4.7-2.7-6.5 -1.8-1.8-4-2.7-6.5-2.7 -2.5 0-4.7 0.9-6.5 2.7 -1.8 1.8-2.7 4-2.7 6.5 0 2.4 0.8 4.5 2.5 6.3 1.7 1.8 3.7 2.7 6.1 2.9l-1.2-2c-0.2-0.3 0.9-1 1.1-0.7l2.3 3.7c0.2 0.3 0 0.6-0.2 0.7L9.5 23.8c-0.3 0.2-0.9-0.9-0.5-1.2l2.1-1.1c-2.7-0.2-5-1.4-6.9-3.4 -1.9-2-2.8-4.5-2.8-7.2 0-3 1.1-5.5 3.1-7.6C6.5 1.2 9 0.2 12 0.2c3 0 5.5 1.1 7.6 3.1C21.7 5.4 22.7 7.9 22.7 10.9z" transform="scale(-1,1) translate(-24,0)" /><path d="M8.1 15.1c-0.1 0-0.1 0-0.1-0.1V8C8 7.7 7.8 7.9 7.7 7.9L6.8 8.3C6.8 8.4 6.7 8.3 6.7 8.2L6.3 7.3C6.2 7.2 6.3 7.1 6.4 7.1l2.7-1.2c0.1 0 0.4 0 0.4 0.3v8.8c0 0.1 0 0.1-0.1 0.1H8.1z" /><path d="M17.7 10.6c0 2.9-1.3 4.7-3.5 4.7 -2.2 0-3.5-1.8-3.5-4.7s1.3-4.7 3.5-4.7C16.4 5.9 17.7 7.7 17.7 10.6zM12.3 10.6c0 2.1 0.7 3.4 2 3.4 1.3 0 2-1.2 2-3.4 0-2.1-0.7-3.4-2-3.4C13 7.2 12.3 8.5 12.3 10.6z" />
732 </g>
733 </svg>
734 <?php
735 }
736
737 add_action( 'wp_footer', 'fv_player_load_svg_buttons_everywhere' );
738
739 function fv_player_load_svg_buttons_everywhere() {
740 global $fv_fp;
741 if( !$fv_fp->should_force_load_js() ) {
742 return;
743 }
744
745 foreach( array(
746 'no_picture',
747 'repeat',
748 'rewind'
749 ) AS $type ) {
750 if( $type == 'rewind' ) {
751 add_action( 'wp_footer', 'fv_player_footer_svg_rewind', 101 );
752 } else if( $type == 'repeat' || $type == 'no_picture' ) {
753 add_action( 'wp_footer', 'fv_player_footer_svg_playlist', 101 );
754 }
755 }
756
757 if( function_exists('FV_Player_Pro') && method_exists( 'FV_Player_Pro', 'svg_chapters') ) {
758 add_action( 'wp_footer', array( FV_Player_Pro(), 'svg_chapters'), 101 );
759 }
760 }
761
762 add_filter( 'script_loader_tag', 'fv_player_js_loader_mark_scripts', PHP_INT_MAX, 2 );
763
764 /**
765 * Disables scroll auotplay
766 *
767 * @return void
768 */
769 function fv_player_disable_scroll_autoplay() {
770 add_filter('fv_flowplayer_conf', 'fv_player_disable_scroll_autoplay');
771 }
772
773 /**
774 * Removes scroll autoplay from conf
775 *
776 * @param array $conf
777 *
778 * @return array $conf
779 */
780 function fv_player_disable_scroll_autoplay_conf($conf) {
781 unset($conf['autoplay_preload']);
782 return $conf;
783 }
784
785 /**
786 * Alters all the script tags related to FV Player, with excetption of the base FV Player library.
787 * The reason is that it's a dependency of most of the modules so then each module would have to be
788 * adjusted to be able to load without it.
789 *
790 * Fancybox lightbox library with additional code is also excluded.
791 *
792 * @param string $tag The original script tag.
793 * @param string $handle The WordPress script handle
794 *
795 * @global object $fv_fp The FV Player plugin instance
796 *
797 * @return string The adjusted script tag
798 */
799 function fv_player_js_loader_mark_scripts( $tag, $handle ) {
800 global $fv_fp;
801 if( is_admin() || isset($_GET['fv_player_loader_skip']) || $fv_fp->_get_option('js-everywhere') || !$fv_fp->_get_option('js-optimize') || flowplayer::is_wp_rocket_setting( 'delay_js' ) ) {
802 return $tag;
803 }
804
805 if(
806 // script ID must start with one of following
807 (
808 stripos($handle,'flowplayer-') === 0 || // process HLS.js and Dash.js, but not the base FV Player library, that one must be present instantly
809 stripos($handle,'fv-player') === 0 ||
810 stripos($handle,'fv_player') === 0 ||
811 'dashjs' === $handle
812
813 // script handle must not be one of
814 ) && !in_array( $handle, array(
815 'fv_player_lightbox' // without this it would be impossible to open the lightbox without hovering the page before it, so it's really a problem on mobile
816 ), true )
817 ) {
818 $tag = str_replace( ' src=', ' data-fv-player-loader-src=', $tag );
819 add_action( 'wp_print_footer_scripts', 'fv_player_js_loader_load', PHP_INT_MAX );
820 }
821 return $tag;
822 }
823
824 /*
825 * Outpput FV Player JS Loader into footer, hooked in in fv_player_js_loader_mark_scripts()
826 */
827 function fv_player_js_loader_load() {
828 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
829 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
830 $filesystem = new WP_Filesystem_Direct( new StdClass() );
831
832 $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? 'dev' : 'min';
833
834 $js = $filesystem->get_contents( dirname(__FILE__).'/../freedom-video-player/fv-player-loader.'.$suffix.'.js' );
835
836 if( !defined('SCRIPT_DEBUG') || !SCRIPT_DEBUG ) {
837 // remove /* comments */
838 $js = preg_replace( '~/\*[\s\S]*?\*/~m', '', $js );
839 // remove whitespace
840 $js = preg_replace( '~\s+~m', ' ', $js );
841 }
842
843 echo '<script data-length="'.strlen($js).'">'.$js.'</script>';
844 }
845
846 /**
847 * @param string $min The minimal version to check - like 7.4.44.727
848 *
849 * @return bool True if the version is at least $min
850 */
851 function fv_player_extension_version_is_min( $min, $extension = 'pro' ) {
852 $version = false;
853 if( $extension == 'pro' ) {
854 global $FV_Player_Pro;
855 if( isset($FV_Player_Pro) && !empty($FV_Player_Pro->version) ) {
856 $version = $FV_Player_Pro->version;
857 }
858
859 } else if( $extension == 'vast' ) {
860 global $FV_Player_VAST;
861 if( isset($FV_Player_VAST) && !empty($FV_Player_VAST->version) ) {
862 $version = $FV_Player_VAST->version;
863 }
864
865 } else if( $extension == 'alternative-sources' ) {
866 global $FV_Player_Alternative_Sources;
867 if( isset($FV_Player_Alternative_Sources) && !empty($FV_Player_Alternative_Sources->version) ) {
868 $version = $FV_Player_Alternative_Sources->version;
869 }
870
871 } else if( $extension == 'ppv' ) {
872 global $FV_Player_PayPerView;
873 if( isset($FV_Player_PayPerView) && !empty($FV_Player_PayPerView->version) ) {
874 $version = $FV_Player_PayPerView->version;
875 }
876
877 } else if( $extension == 'ppv-woocommerce' ) {
878 global $FV_Player_PayPerView_WooCommerce;
879 if( isset($FV_Player_PayPerView_WooCommerce) && !empty($FV_Player_PayPerView_WooCommerce->version) ) {
880 $version = $FV_Player_PayPerView_WooCommerce->version;
881 }
882
883 }
884
885 $version = str_replace('.beta','',$version);
886
887 return version_compare($version,$min ) != -1;
888 }
889
890
891 /*
892 * WP Rocket Used CSS exclusion
893 * Since many FV Player features are only visible once the video starts this optimization doesn't work
894 */
895 add_filter( 'pre_get_rocket_option_remove_unused_css_safelist', 'fv_player_wp_rocket_used_css' );
896
897 function fv_player_wp_rocket_used_css( $safelist ) {
898 // Without this our additions would show on WP Rocket settings page
899 if ( did_action( 'admin_head-settings_page_wprocket' ) ) {
900 return $safelist;
901 }
902
903 $safelist[] = '/wp-content/fv-player-custom/style-(.*)';
904 $safelist[] = '/wp-content/plugins/fv-wordpress-flowplayer(.*)';
905 $safelist[] = '/wp-content/plugins/fv-player(.*)';
906 return $safelist;
907 }
908
909
910 /*
911 * SiteGround Security "Lock and Protect System Folders" exclusion
912 *
913 * The plugins normally blocks direct PHP calls in wp-content folder, we allow track.php requests for FV Player tracking this way
914 * Unfortunately it uses simple rule like <Files track.php> so we cannot include the folder name.
915 */
916 add_filter( 'sgs_whitelist_wp_content' , 'fv_player_sgs_whitelist_wp_content' );
917
918 function fv_player_sgs_whitelist_wp_content( $exclusions ) {
919 global $fv_fp;
920
921 $exclusions[] = 's3-ajax.php';
922
923 if( $fv_fp->_get_option('video_stats_enable') ) {
924 $exclusions[] = 'track.php';
925 }
926
927 if ( class_exists( 'FV_Player_Coconut' ) ) {
928 $exclusions[] = 'coconut-ajax.php';
929 }
930
931 if ( class_exists( 'FV_Player_Pro' ) ) {
932 $exclusions[] = 'stream-loader.php';
933 }
934
935 return $exclusions;
936 }
937
938
939 /*
940 * SiteGround Optimizer unfortunately does not process the scripts enqueued after wp_head has finished.
941 * So then "Defer Render-blocking JavaScript" does not defer FV Player but it defect jQuery.
942 * So we have to make sure jQuery is not defered as FV Player scripts depend on it.
943 * There are no issues when using "Combine JavaScript Files"
944 */
945 add_filter( 'sgo_js_async_exclude', 'fv_player_sgo_js_async_exclude' );
946
947 function fv_player_sgo_js_async_exclude( $excluded_scripts ) {
948 $excluded_scripts[] = 'jquery-core';
949 return $excluded_scripts;
950 }
951
952 /*
953 * Some themes do not remove shortcodes when showing excerpts.
954 * So we remove [fvplayer...] as in excerpt it would only show "Please enable JavaScript" kind of message.
955 */
956
957 // Learnify, this filter seems to run for excerpts only, see learnify_show_post_content()
958 add_filter( 'learnify_filter_post_content', 'fv_player_remove_for_excerpt' );
959
960 function fv_player_remove_for_excerpt( $post_content ) {
961 if( is_archive() ) {
962 $post_content = preg_replace( '~\[fvplayer.*?\]~', '', $post_content );
963 }
964 return $post_content;
965 }
966
967
968 /*
969 * @param array $args {
970 * @param int $count Number of items to get
971 * @param bool $full_details Should it return full details about the video progress?
972 * @param string $include Get only "unfinished" or "finished" videos.
973 * @param string $post_type Post type where the video is embed
974 * @param int $user_id User ID to operate on, defaults to logged in user.
975 * }
976 *
977 * @return array Array of post IDs
978 */
979 function fv_player_get_user_watched_post_ids( $args = array() ) {
980 $args = wp_parse_args( $args, array(
981 'count' => 20,
982 'include' => 'all',
983 'post_type' => 'any',
984 'user_id' => get_current_user_id()
985 ) );
986
987 $args['full_details'] = true;
988
989 $video_ids = fv_player_get_user_watched_video_ids( $args );
990 if( count($video_ids) == 0 ) {
991 return array();
992 }
993
994 $post_ids = array();
995 foreach( $video_ids AS $data ) {
996 if( !empty($data['post_id']) ) {
997 $post_ids[] = $data['post_id'];
998 }
999 }
1000
1001 return $post_ids;
1002 }
1003
1004
1005 /*
1006 * @param array $args {
1007 * @param int $count Number of items to get
1008 * @param bool $full_details Should it return full details about the video progress?
1009 * @param string $include Get only "unfinished" or "finished" videos.
1010 * @param string $post_type Post type where the video is embed
1011 * @param int $user_id User ID to operate on, defaults to logged in user.
1012 * }
1013 *
1014 * @return array Array of video IDs, or array of video progress details
1015 */
1016 function fv_player_get_user_watched_video_ids( $args = array() ) {
1017 $args = wp_parse_args( $args, array(
1018 'count' => 20,
1019 'full_details' => false,
1020 'include' => 'all',
1021 'post_type' => 'any',
1022 'user_id' => get_current_user_id()
1023 ) );
1024
1025 $output = array();
1026
1027 global $wpdb;
1028
1029 static $user_meta;
1030 if( !isset($user_meta) ) {
1031 $user_meta = array();
1032 }
1033 if( !isset($user_meta[ $args['user_id'] ]) ) {
1034 $user_meta[ $args['user_id'] ] = $wpdb->get_results(
1035 $wpdb->prepare(
1036 "SELECT meta_key, meta_value FROM $wpdb->usermeta WHERE user_id = %d AND ( meta_key LIKE %s OR meta_key LIKE %s OR meta_key LIKE %s ) ORDER BY umeta_id DESC",
1037 $args['user_id'],
1038 $wpdb->esc_like( 'fv_wp_flowplayer_position_' ) . '%',
1039 $wpdb->esc_like( 'fv_wp_flowplayer_top_position_' ) . '%',
1040 $wpdb->esc_like( 'fv_wp_flowplayer_saw_' ) . '%'
1041 )
1042 );
1043 }
1044
1045 if( $user_meta[ $args['user_id'] ] ) {
1046 $metas = $user_meta[ $args['user_id'] ];
1047
1048 $output = array();
1049
1050 foreach( $metas AS $meta ) {
1051 if( stripos( $meta->meta_key, 'fv_wp_flowplayer_top_position_') === 0 ) {
1052 $video_id = str_replace( 'fv_wp_flowplayer_top_position_', '', $meta->meta_key );
1053 if( !is_numeric($video_id) ) continue;
1054
1055 $output[$video_id] = array(
1056 'type' => 'unfinished',
1057 'message' => 'Watched until '.flowplayer::format_hms($meta->meta_value),
1058 'time' => $meta->meta_value
1059 );
1060
1061 } else if( stripos( $meta->meta_key, 'fv_wp_flowplayer_position_') === 0 ) {
1062 $video_id = str_replace( 'fv_wp_flowplayer_position_', '', $meta->meta_key );
1063 if( !is_numeric($video_id) ) continue;
1064
1065 if( empty($output[$video_id]) ) {
1066 $output[$video_id] = array(
1067 'type' => 'unfinished',
1068 'message' => 'Watched until '.flowplayer::format_hms($meta->meta_value),
1069 'time' => $meta->meta_value
1070 );
1071 }
1072
1073 }
1074
1075 // No "else if" as we want the full video watch to take priority over the stored position
1076 if( stripos( $meta->meta_key, 'fv_wp_flowplayer_saw_') === 0 ) {
1077 $video_id = str_replace( 'fv_wp_flowplayer_saw_', '', $meta->meta_key );
1078 if( !is_numeric($video_id) ) continue;
1079
1080 $output[$video_id] = array(
1081 'type' => 'finished',
1082 'message' => 'Saw whole video'
1083 );
1084 }
1085 }
1086 }
1087
1088 // Filter unfinished or finished videos
1089 if( $args['include'] != 'all' ) {
1090 foreach( $output AS $video_id => $details ) {
1091 if( $args['include'] != $details['type'] ) {
1092 unset( $output[$video_id]);
1093 }
1094 }
1095 }
1096
1097 // Add player_id and post_id for the videos
1098 // Remove items which do not belong to a player and published post
1099 if( count($output) ) {
1100 $post_type_where = $args['post_type'] != 'any' ? 'post_type = %s AND ': '';
1101 $video_ids = implode( ',', array_map( 'intval', array_keys( $output ) ) );
1102
1103 $videos2players2posts = $wpdb->get_results( $wpdb->prepare( "
1104 SELECT pl.id AS player_id, v.id AS video_id, pm.meta_value AS post_id FROM
1105 {$wpdb->prefix}fv_player_players AS pl
1106 JOIN {$wpdb->prefix}fv_player_playermeta AS pm
1107 ON pl.id = pm.id_player
1108 JOIN {$wpdb->prefix}fv_player_videos AS v
1109 ON FIND_IN_SET(v.id, pl.videos)
1110 JOIN {$wpdb->posts} AS p
1111 ON p.ID = pm.meta_value
1112 WHERE
1113 pm.meta_key = 'post_id' AND
1114 p.post_status = 'publish' AND
1115 $post_type_where
1116 v.id IN ( {$video_ids} )", $args['post_type'] ) );
1117
1118 foreach( $output AS $video_id => $details ) {
1119 $found = false;
1120 foreach( $videos2players2posts AS $more_details ) {
1121
1122 if( $more_details->video_id == $video_id ) {
1123 $found = true;
1124 $output[$video_id]['player_id'] = $more_details->player_id;
1125 $output[$video_id]['post_id'] = $more_details->post_id;
1126 break;
1127 }
1128 }
1129
1130 if( !$found ) {
1131 unset($output[$video_id]);
1132 }
1133 }
1134 }
1135
1136 if( !$args['full_details'] ) {
1137 $output = array_keys( $output );
1138 }
1139
1140 return $output;
1141 }
1142
1143
1144 add_shortcode( 'fvplayer_editor', 'fvplayer_editor' );
1145
1146 /**
1147 * @param array $args {
1148 * @type string $field jQuery field selector of the field with the shortcode
1149 * @type string $hide Comma separated list of fields to hide
1150 * @type string $library Comma separated list of libraries to show in Media Library
1151 * @type string $tabs Use "none" to only show Playlist and Videos tabs and nothing else, like Options, Actions or Subtitles
1152 * }
1153 *
1154 * @return string HTML code.
1155 */
1156 function fvplayer_editor( $args ) {
1157 include_once( ABSPATH.'/wp-admin/includes/plugin.php' );
1158 include_once( __DIR__.'/editor.php' );
1159
1160 if( version_compare(phpversion(),'5.5.0') != -1 ) {
1161 include_once( __DIR__ . '/../models/media-browser.php');
1162 }
1163
1164 if ( function_exists( 'FV_Player_Pro' ) && method_exists ( 'FV_Player_Pro', 'include_vimeo_media_browser' ) ) {
1165 FV_Player_Pro()->include_vimeo_media_browser( true );
1166 }
1167
1168 do_action( 'fvplayer_editor_load' );
1169
1170 wp_enqueue_media();
1171
1172 $args['frontend'] = true;
1173
1174 fv_player_shortcode_editor_scripts_enqueue( $args );
1175
1176 ob_start();
1177 fv_wp_flowplayer_edit_form_after_editor();
1178 ?>
1179 <script>
1180 var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
1181
1182 document.addEventListener( 'DOMContentLoaded', function() {
1183 jQuery(function() {
1184
1185 // Wait until FV Player Editor $doc.ready() finishes
1186 setTimeout( function() {
1187 fv_player_editor.editor_open();
1188 });
1189 });
1190 });
1191 </script>
1192 <style>
1193 #fv-player-editor-modal {
1194 display: block !important;
1195 position: relative !important;
1196 top: auto !important;
1197 left: unset !important;
1198 right: unset !important;
1199 bottom: unset !important;
1200 z-index: unset !important;
1201 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
1202 }
1203 #fv-player-editor-modal h2 {
1204 letter-spacing: normal;
1205 }
1206 #fv-player-editor-modal #fv-player-shortcode-editor {
1207 position: static !important;
1208 }
1209 #fv-player-editor-modal-close {
1210 display: none !important;
1211 }
1212 #fv-player-shortcode-editor-preview {
1213 margin-top: 16px;
1214 }
1215 .fv-player-tab {
1216 overflow-y: unset !important;
1217 }
1218 /* Left-align for the inputs */
1219 #fv-player-shortcode-editor #fv-player-shortcode-editor-preview-no .components-base-control__field {
1220 justify-content: unset;
1221 }
1222 /* Hide the intro text */
1223 #fv-player-shortcode-editor-preview-no > p {
1224 display: none;
1225 }
1226 /* Somehow the vertical scrolling would appear when one video was inserted */
1227 #fv-player-shortcode-editor #fv-player-shortcode-editor-left {
1228 overflow-x: unset;
1229 }
1230
1231 /* Core WordPress Admin Button styling */
1232 #fv-player-editor-modal .button, #fv-player-editor-modal .button-primary, #fv-player-editor-modal .button-secondary {
1233 box-shadow: unset;
1234 display: inline-block;
1235 text-decoration: none;
1236 font-size: 13px;
1237 font-weight: normal;
1238 letter-spacing: normal;
1239 line-height: 2.15384615;
1240 min-height: 30px;
1241 margin: 0;
1242 margin-top: 0 !important;
1243 padding: 0 10px;
1244 cursor: pointer;
1245 border-width: 1px;
1246 border-style: solid;
1247 -webkit-appearance: none;
1248 border-radius: 3px;
1249 white-space: nowrap;
1250 box-sizing: border-box;
1251 text-shadow: unset;
1252 text-transform: none;
1253 width: auto !important;
1254 }
1255 #fv-player-editor-modal .button.hover, #fv-player-editor-modal .button:hover, #fv-player-editor-modal .button-secondary:hover {
1256 background: #f0f0f1;
1257 border-color: #0a4b78;
1258 color: #0a4b78;
1259 }
1260 #fv-player-editor-modal .button, #fv-player-editor-modal .button-secondary {
1261 color: #2271b1;
1262 border-color: #2271b1;
1263 background: #f6f7f7;
1264 vertical-align: top;
1265 }
1266 #fv-player-editor-modal .button-primary {
1267 background: #2271b1;
1268 border-color: #2271b1;
1269 color: #fff;
1270 text-decoration: none;
1271 text-shadow: none;
1272 }
1273 #fv-player-editor-modal .button-primary.hover, #fv-player-editor-modal .button-primary:hover, #fv-player-editor-modal .button-primary.focus, #fv-player-editor-modal .button-primary:focus {
1274 background: #135e96;
1275 border-color: #135e96;
1276 color: #fff;
1277 }
1278
1279 /* Core WordPress Media Library styles to fix */
1280 .media-modal .media-router button.media-menu-item {
1281 color: #3c434a;
1282 text-transform: none;
1283 }
1284 </style>
1285 <?php
1286 return ob_get_clean();
1287 }
1288
1289 /**
1290 * The nonce normally only work up to 24 hours, but it might just be 12 hours.
1291 *
1292 * We set the nonce life to 7 days to make sure caching plugins don't break the video tracking etc.
1293 *
1294 * So far we were able to use 42 hours old nonce without any issues. When the nonce was 4 days and 19 hours old
1295 * it would already fail. So my guess is that this way we can be sure that the nonce is valid for 3.5 days,
1296 * but it might be up to 7 days.
1297 */
1298 add_filter( 'nonce_life', 'fv_player_frontend_nonce_life', PHP_INT_MAX, 2 );
1299
1300 /**
1301 * @param int $seconds
1302 * @param string|false $action This has one been added in WordPress 6.1 unfortunately
1303 *
1304 * @return int Longer nonce TTL if it's used by FV Player
1305 */
1306 function fv_player_frontend_nonce_life( $seconds, $action = false ) {
1307 if (
1308 in_array(
1309 $action,
1310 array(
1311 'fv_player_email_signup',
1312 'fv_player_track',
1313 'fv_player_video_position_save',
1314 )
1315 )
1316 ) {
1317 $seconds = 7 * DAY_IN_SECONDS;
1318 }
1319 return $seconds;
1320 }
1321