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 / models / checker.php

checker.php in FV Player 8 8.0.21, at models/checker.php

564 lines 20.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 class FV_Player_Checker {
20
21
22 var $is_cron = false;
23
24
25 function __construct() {
26 add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) );
27 add_action( 'fv_flowplayer_checker_event', array( $this, 'checker_cron' ) );
28 add_action( 'init', array( $this, 'cron_init' ) );
29 }
30
31
32
33 public static function check_headers( $headers, $remotefilename, $random, $args = false ) {
34 $args = wp_parse_args( $args, array( 'talk_bad_mime' => 'Video served with a bad mime type' , 'wrap'=>'p' ) );
35
36 $sOutput = '';
37
38 $video_errors = array();
39
40 $bFatal = false;
41 if( $headers && $headers['response']['code'] == '404' ) {
42 $video_errors[] = 'File not found (HTTP 404)!';
43 $bFatal = true;
44 } else if( $headers && $headers['response']['code'] == '403' ) {
45 $video_errors[] = 'Access to video forbidden (HTTP 403)!';
46 $bFatal = true;
47 } else if( $headers && $headers['response']['code'] != '200' && $headers['response']['code'] != '206' ) {
48 $video_errors[] = 'Can\'t check the video (HTTP '.$headers['response']['code'].')!';
49 $bFatal = true;
50 } else {
51
52 if(
53 ( !isset($headers['headers']['accept-ranges']) || $headers['headers']['accept-ranges'] != 'bytes' ) &&
54 !isset($headers['headers']['content-range'])
55 ) {
56 $video_errors[] = 'Server does not support HTTP range requests! Please check <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq#getting-error-about-range-requests">our FAQ</a>.';
57 }
58
59 if(
60 ( stripos( $remotefilename, '.mp4' ) !== FALSE && $headers['headers']['content-type'] != 'video/mp4' ) ||
61 ( stripos( $remotefilename, '.m4v' ) !== FALSE && $headers['headers']['content-type'] != 'video/x-m4v' ) ||
62 ( stripos( $remotefilename, '.webm' ) !== FALSE && $headers['headers']['content-type'] != 'video/webm' ) ||
63 ( stripos( $remotefilename, '.mov' ) !== FALSE && $headers['headers']['content-type'] != 'video/mp4' )
64 ) {
65 if( stripos( $remotefilename, '.mov' ) !== FALSE ) {
66 $meta_note_addition = ' Firefox on Windows does not like MOV files with video/quicktime mime type.';
67 } else if( stripos( $remotefilename, '.webm' ) !== FALSE ) {
68 $meta_note_addition = ' Older Firefox versions don\'t like WEBM files with mime type other than video/webm.';
69 } else {
70 $meta_note_addition = ' Some web browsers may experience playback issues in HTML5 mode (Internet Explorer 9 - 10).';
71 /*if( $fv_fp->conf['engine'] == 'default' ) {
72 $meta_note_addition .= ' Currently you are using the "Default (mixed)" <a href="'.site_url().'/wp-admin/admin.php?page=fvplayer">Preferred Flowplayer engine</a> setting, so IE will always use Flash and will play fine.';
73 }*/
74 }
75
76 $fix = '<div class="fix-meta-'.$random.'" style="display: none; ">
77 <p>If the video is hosted on Amazon S3:</p>
78 <blockquote>Using your Amazon AWS Management Console, you can go though your videos and find file content type under the "Metadata" tab in an object\'s "Properties" pane and fix it to "video/mp4" for MP4, "video/x-m4v" for M4V files, "video/mp4" for MOV files and "video/webm" for WEBM files.</blockquote>
79 <p>If the video is hosted on your server, put this into your .htaccess:</p>
80 <pre>AddType video/mp4 .mp4
81 AddType video/webm .webm
82 AddType video/ogg .ogv
83 AddType application/x-mpegurl .m3u8
84 AddType video/x-m4v .m4v
85 AddType video/mp4 .mov
86 # hls transport stream segments:
87 AddType video/mp2t .ts</pre>
88 <p>If you are using Microsoft IIS, you need to use the IIS manager. Check our <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq#video-doesnt-play-internet-explorer" target="_blank">FAQ</a> for more info.</p>
89 </div>';
90
91 $sOutput = ( $args['wrap'] ) ? '<'.$args['wrap'].'>' : '';
92 $sOutput .= '<strong>Bad mime type</strong>: '.$args['talk_bad_mime'].' <tt>'.$headers['headers']['content-type'].'</tt>!'.$meta_note_addition.' (<a href="#" onclick="jQuery(\'.fix-meta-'.$random.'\').toggle(); return false">show fix</a>)';
93 $sOutput .= ( $args['wrap'] ) ? '</'.$args['wrap'].'>' : '';
94 $sOutput .= $fix;
95 $video_errors[] = $sOutput;
96 }
97 }
98
99 return array( $video_errors, (isset($headers['headers']['content-type'])) ? $headers['headers']['content-type'] : '', $bFatal );
100 }
101
102
103
104
105 public function check_mimetype( $URLs = false, $meta = array(), $force_is_cron = false ) {
106
107 $error = false;
108 $tStart = microtime(true);
109
110 global $fv_wp_flowplayer_ver, $fv_fp;
111
112 if( !empty($meta) ) {
113 extract( $meta, EXTR_SKIP );
114 }
115
116 if( defined('DOING_AJAX') && DOING_AJAX && isset( $_POST['media'] ) && stripos( sanitize_url( $_SERVER['HTTP_REFERER'] ), home_url() ) === 0 ) {
117 $URLs = json_decode( stripslashes( trim( wp_strip_all_tags( $_POST['media'] ) ) ) );
118 }
119
120 if( isset($URLs) ) {
121 $all_sources = $URLs;
122
123 foreach( $all_sources AS $source ) {
124 if( preg_match( '~^https?://~', $source, $match ) ) {
125 $media = $source;
126 break;
127 }
128 }
129
130 $random = (isset($_POST['hash'])) ? trim( wp_strip_all_tags( sanitize_text_field( $_POST['hash'] ) ) ) : false;
131 if( isset($media) ) {
132 $remotefilename = $media;
133 $remotefilename_encoded = flowplayer::get_encoded_url($remotefilename);
134
135 $bValidFile = true;
136
137 if ( ! class_exists( 'getID3' ) ) {
138 require( ABSPATH . WPINC . '/ID3/getid3.php' );
139 }
140 $getID3 = new getID3;
141
142 $upload_dir = wp_upload_dir();
143 $localtempfilename = trailingslashit( $upload_dir['basedir'] ).'fv_flowlayer_tmp_'.md5(wp_rand(1,999)).'_'.basename( substr($remotefilename_encoded,0,32) );
144
145 global $wp_filesystem;
146
147 if ( is_null( $wp_filesystem ) ) {
148 require_once ABSPATH . '/wp-admin/includes/file.php';
149 WP_Filesystem();
150 }
151
152 $real_file_size = 0;
153 $analysis_size = 8 * 1024 * 1024;
154
155 $remotefilename_encoded = $fv_fp->get_video_src( $remotefilename_encoded, array( 'dynamic' => true ) );
156
157 $res = wp_remote_get( $remotefilename_encoded, array(
158 'headers' => array(
159 'range' => 'bytes=0-' . $analysis_size,
160 'referer' => home_url()
161 ),
162 'timeout' => !$this->is_cron && !$force_is_cron ? apply_filters( 'fv_flowplayer_checker_timeout_quick', 2 ) : 20,
163 'user-agent' => 'FV Player video checker/' . $fv_wp_flowplayer_ver
164 ) );
165
166 if ( ! is_wp_error( $res ) ) {
167
168 // Get the real file size out of Content-length: 0-8388608/{full file size here}
169 if ( $content_range = wp_remote_retrieve_header( $res, 'content-range' ) ) {
170 $content_range_parts = explode( '/', $content_range );
171 if ( 2 === count( $content_range_parts ) ) {
172 $real_file_size = intval( $content_range_parts[1] );
173 }
174 }
175
176 $wp_filesystem->put_contents( $localtempfilename, wp_remote_retrieve_body( $res ) );
177
178 if( !empty($res['response']['code']) ) {
179 $code = intval($res['response']['code']);
180 if( $code == 404 ) {
181 $error = 'Video not found';
182
183 } else if( $code == 403 ) {
184 $error = 'Access denied';
185
186 } else if( $code > 399 ) {
187 $error = 'HTTP '.$code;
188 if( !empty($res['response']['message']) ) {
189 $error .= ': '.$res['response']['message'];
190 }
191 }
192 }
193
194 list( $aVideoErrors, $sContentType, $bFatal ) = $this->check_headers( $res, $remotefilename, $random );
195 if( $bFatal ) {
196 $bValidFile = false;
197 }
198
199 if( $bValidFile ) {
200 $ThisFileInfo = $getID3->analyze( $localtempfilename );
201 }
202
203 $wp_filesystem->delete( $localtempfilename );
204
205 } else {
206 $bValidFile = false;
207 }
208
209 /*
210 Only check file length
211 */
212
213 if( (isset($meta_action) && $meta_action == 'check_time') || $force_is_cron ) {
214 $time = false;
215 $width = false;
216 $height = false;
217
218 if( isset($ThisFileInfo) && isset($ThisFileInfo['playtime_seconds']) ) {
219 $time = $ThisFileInfo['playtime_seconds'];
220
221 /**
222 * MP3 duration check is not reliable if we only check a part of file.
223 * So here we multiply the duration by the ratio of the real file size to the analysis size,
224 * if it's bigger than the analysis size.
225 */
226 if ( stripos( $remotefilename_encoded, '.mp3' ) !== false ) {
227 if ( $real_file_size > $analysis_size ) {
228 $time = $time * $real_file_size / $analysis_size;
229 }
230 }
231 }
232 if( !empty($ThisFileInfo['video']['resolution_x']) ) {
233 $width = intval($ThisFileInfo['video']['resolution_x']);
234 }
235 if( !empty($ThisFileInfo['video']['resolution_y']) ) {
236 $height = intval($ThisFileInfo['video']['resolution_y']);
237 }
238
239 $is_live = false;
240 $is_encrypted = false;
241
242 if(preg_match('/.m3u8(\?.*)?$/i', $remotefilename_encoded)){
243 $remotefilename_encoded = apply_filters( 'fv_flowplayer_video_src', $remotefilename_encoded , array('dynamic'=>true) );
244 $request = wp_remote_get(
245 $remotefilename_encoded,
246 array(
247 'headers' => array(
248 'referer' => home_url(),
249 ),
250 'timeout' => 15,
251 'user-agent' => 'FV Player video checker/' . $fv_wp_flowplayer_ver
252 )
253 );
254
255 $response_code = wp_remote_retrieve_response_code( $request );
256 if( $response_code == 404 ) {
257 return array( 'error' => 'Video not found' );
258
259 } else if( $response_code == 403 ) {
260 return array( 'error' => 'Access denied' );
261
262 } else if( is_wp_error($request) ) {
263 return array( 'error' => $request->get_error_message() );
264 }
265
266 $response = wp_remote_retrieve_body( $request );
267
268 $playlist = false;
269 $duration = 0;
270 $segments = false;
271
272 if(preg_match_all('/^#EXTINF:([0-9]+\.?[0-9]*)/im', $response,$segments)){
273 $is_live = stripos( $response, '#EXT-X-ENDLIST' ) === false;
274
275 foreach($segments[1] as $segment_item){
276 $duration += $segment_item;
277 }
278 } else {
279 $lines = explode( "\n", $response );
280
281 $streams = array();
282 $had_ext_x_stream_inf = false;
283 $resoluton_x_max = 0;
284 $resoluton_y_max = 0;
285
286 foreach( $lines AS $line ) {
287 // last line was starting with #EXT-X-STREAM-INF:
288 if( stripos($line,'#') !== 0 && $had_ext_x_stream_inf ) {
289 $streams[] = trim($line);
290 $had_ext_x_stream_inf = false;
291 }
292
293 if( stripos($line,'#EXT-X-STREAM-INF:') === 0 ) {
294 $had_ext_x_stream_inf = true;
295
296 if( stripos($line,'RESOLUTION=') !== false ) {
297 if( preg_match( '~RESOLUTION=(\d+)x(\d+)~', $line, $resoluton ) ) {
298 if( $resoluton[1] > $resoluton_x_max ) {
299 $resoluton_x_max = $resoluton[1];
300 }
301 if( $resoluton[2] > $resoluton_y_max ) {
302 $resoluton_y_max = $resoluton[2];
303 }
304 }
305 }
306 }
307
308 }
309
310 $width = $resoluton_x_max;
311 $height = $resoluton_y_max;
312
313 foreach($streams as $item){
314 $item_url = $item;
315
316 // If the stream URL is relative, we need to take the master playlist URL, remove the filename part and put in the stream link
317 if( stripos( $item_url, 'http://' ) !== 0 && stripos( $item_url, 'https://' ) !== 0 ) {
318 $item_url = preg_replace('/[^\/]*\.m3u8(\?.*)?/i', $item, $remotefilename_encoded);
319 }
320 if( $secured_url = $fv_fp->get_video_src( $item_url, array( 'dynamic' => true ) ) ) {
321 $item_url = $secured_url;
322 }
323
324 $request = wp_remote_get(
325 $item_url,
326 array(
327 'headers' => array(
328 'referer' => home_url()
329 ),
330 'user-agent' => 'FV Player video checker/' . $fv_wp_flowplayer_ver
331 )
332 );
333
334 if( is_wp_error($request) ) {
335 return array( 'error' => $request->get_error_message() );
336 }
337
338 $playlist_item = wp_remote_retrieve_body( $request );
339
340 if(preg_match_all('/^#EXTINF:([0-9]+\.?[0-9]*)/im', $playlist_item,$segments)){
341 $is_live = stripos( $playlist_item, '#EXT-X-ENDLIST' ) === false;
342 $is_encrypted = stripos( $playlist_item, '#EXT-X-KEY' ) !== false;
343
344 foreach($segments[1] as $segment_item){
345 $duration += $segment_item;
346 }
347 }
348 if($duration > 0)
349 break;
350 }
351 }
352
353 $time = $duration;
354 }
355
356 $time = apply_filters( 'fv_flowplayer_checker_time', $time, $remotefilename_encoded );
357 $key = flowplayer::get_video_key($remotefilename_encoded);
358
359 global $post;
360 $fv_flowplayer_meta = array();
361 if( !empty($post) ) {
362 $fv_flowplayer_meta = get_post_meta( $post->ID, $key, true );
363 if( !$fv_flowplayer_meta ) $fv_flowplayer_meta = array();
364 }
365
366 $fv_flowplayer_meta['error'] = $error;
367 $fv_flowplayer_meta['duration'] = $time;
368 $fv_flowplayer_meta['width'] = $width;
369 $fv_flowplayer_meta['height'] = $height;
370 $fv_flowplayer_meta['is_live'] = $is_live;
371 $fv_flowplayer_meta['is_encrypted'] = $is_encrypted;
372 $fv_flowplayer_meta['etag'] = ! is_wp_error( $res ) && isset($res['headers']['etag']) ? $res['headers']['etag'] : false; // todo: check!
373 $fv_flowplayer_meta['date'] = time();
374 $fv_flowplayer_meta['check_time'] = microtime(true) - $tStart;
375
376 if( $time > 0 || $error || $this->is_cron ) {
377 if( !empty($post) ) {
378 update_post_meta( $post->ID, $key, $fv_flowplayer_meta );
379 }
380 return $fv_flowplayer_meta;
381 }
382
383 }
384
385 } // end isset($media)
386 }
387 }
388
389
390
391
392 function checker_cron() {
393 global $fv_fp;
394 if( $fv_fp->_get_option('video_model_db_checked') && $fv_fp->_get_option('video_meta_model_db_checked') ) {
395
396 // get all video IDs for which the duration is zero and are not live streams and were not checked in last day
397 global $wpdb;
398 $aVideos = $wpdb->get_col( "SELECT id FROM `{$wpdb->prefix}fv_player_videos` WHERE duration = 0 AND live = 0 AND DATE_SUB( UTC_TIMESTAMP(), INTERVAL 1 DAY ) > last_check ORDER BY id DESC" );
399
400 if( $aVideos ) {
401 global $FV_Player_Db;
402 foreach( $aVideos AS $video_id ) {
403 $objVideo = new FV_Player_Db_Video( $video_id, array(), $FV_Player_Db );
404
405 $last_check = $objVideo->getLastCheck();
406
407 $check_ttl = 86400;
408
409 $is_live = $objVideo->getLive();
410 if( $is_live && FV_Player_YouTube()->is_youtube( $objVideo->getSrc() ) ) {
411 $check_ttl = 300;
412 }
413
414 if( $last_check && intval($last_check) + $check_ttl > time() ) {
415 continue;
416 }
417
418 $error_count = $objVideo->getMetaValue('error_count',true);
419
420 if( $error_count && intval($error_count) > 5 ) {
421 continue;
422 }
423
424 // Make sure we do no loose video meta!
425 $objVideo->save( array(), true );
426
427 }
428 }
429 }
430
431 // legacy
432 if( !$aQueue = self::queue_get() ) return;
433 $tStart = microtime(true);
434 $this->is_cron = true;
435 foreach( $aQueue AS $key => $item ) {
436 if( microtime(true) - $tStart > apply_filters( 'fv_flowplayer_checker_cron_time', 20 ) ) {
437 break;
438 }
439 global $post;
440 $tmp = $post;
441 $post = get_post($key);
442
443 do_action( 'fv_flowplayer_checker_cron_post', $key );
444
445 fv_wp_flowplayer_save_post($key);
446 $post = $tmp;
447 }
448
449 }
450
451
452
453
454 function cron_init() {
455 if ( !wp_next_scheduled( 'fv_flowplayer_checker_event' ) ) {
456 wp_schedule_event( time(), '5minutes', 'fv_flowplayer_checker_event' );
457 }
458 }
459
460
461
462
463 function cron_schedules( $schedules ) {
464 $schedules['5minutes'] = array(
465 'interval' => 300,
466 'display' => __('Every 5 minutes')
467 );
468 return $schedules;
469 }
470
471
472
473
474 public static function get_videos( $post_id ) {
475 global $fv_fp;
476
477 $objPost = get_post($post_id);
478 if( $objPost ) {
479 $content = $objPost->post_content;
480 preg_match_all( '~\[(?:flowplayer|fvplayer).*?\]~', $content, $matches );
481
482 $aMeta = get_post_custom($post_id);
483 if( $aMeta && is_array($aMeta) && count($aMeta) > 0) {
484 $meta_values = '';
485 foreach( $aMeta AS $values ) {
486 $meta_values .= implode("", $values);
487 }
488 if( preg_match_all( '~\[(?:flowplayer|fvplayer).*?\]~', $meta_values, $meta_matches ) ) {
489 $matches[0] = array_merge($matches[0], $meta_matches[0]);
490 }
491 }
492
493 }
494
495 $videos = array();
496 if( isset($matches[0]) && count($matches[0]) ) {
497 $aPlaylistItems = array();
498 foreach( $matches[0] AS $shortcode ) {
499 $aArgs = shortcode_parse_atts( rtrim($shortcode,']') );
500 list( $playlist_items_external_html, $aPlaylistItems ) = $fv_fp->build_playlist( $aArgs, isset($aArgs['src']) ? $aArgs['src'] : false, false, false, false, false, true );
501
502 if( count($aPlaylistItems) > 0 ) {
503 foreach( $aPlaylistItems AS $aItem ) {
504 if( isset($aItem['sources']) && isset($aItem['sources'][0]) && isset($aItem['sources'][0]['src']) ) {
505 $videos[] = $aItem['sources'][0]['src'];
506 }
507 }
508 }
509 }
510 }
511
512 if( count($videos) > 0 ) {
513 $videos = array_unique($videos);
514 } else {
515 $videos = false;
516 }
517 return $videos;
518 }
519
520
521
522
523 public static function queue_add( $post_id ) {
524 $aQueue = get_option( 'fv_flowplayer_checker_queue' ) ? get_option( 'fv_flowplayer_checker_queue' ) : array();
525 $aQueue[$post_id] = true;
526 update_option( 'fv_flowplayer_checker_queue', $aQueue );
527 }
528
529
530
531
532 public static function queue_check( $post_id = false ) {
533 global $post;
534 $post_id = ( isset($post->ID) ) ? $post->ID : $post_id;
535 $aQueue = get_option( 'fv_flowplayer_checker_queue' ) ? get_option( 'fv_flowplayer_checker_queue' ) : array();
536 if( in_array($post_id,array_keys($aQueue)) ) {
537 return true;
538 }
539 return false;
540 }
541
542
543
544
545 public static function queue_get() {
546 return get_option( 'fv_flowplayer_checker_queue', array() );
547 }
548
549
550
551
552 public static function queue_remove( $post_id ) {
553 $aQueue = get_option( 'fv_flowplayer_checker_queue' ) ? get_option( 'fv_flowplayer_checker_queue' ) : array();
554 if( isset($aQueue[$post_id]) ) {
555 unset($aQueue[$post_id]);
556 }
557 update_option( 'fv_flowplayer_checker_queue', $aQueue );
558 }
559
560
561
562
563 }
564