PluginProbe
FV Player 8 / trunk
FV Player 8 vtrunk
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 trunk, at controller/frontend.php

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