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

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

569 lines 20.2 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 // If we create new player in FV Player Coconut in coconut-ajax.php, there will be no wp_remote_get function or WP_Http class, so we cannot check the video.
108 if ( ! function_exists( 'wp_remote_get' ) || ! class_exists( 'WP_Http' ) ) {
109 return false;
110 }
111
112 $error = false;
113 $tStart = microtime(true);
114
115 global $fv_wp_flowplayer_ver, $fv_fp;
116
117 if( !empty($meta) ) {
118 extract( $meta, EXTR_SKIP );
119 }
120
121 if( defined('DOING_AJAX') && DOING_AJAX && isset( $_POST['media'] ) && stripos( sanitize_url( $_SERVER['HTTP_REFERER'] ), home_url() ) === 0 ) {
122 $URLs = json_decode( stripslashes( trim( wp_strip_all_tags( $_POST['media'] ) ) ) );
123 }
124
125 if( isset($URLs) ) {
126 $all_sources = $URLs;
127
128 foreach( $all_sources AS $source ) {
129 if( preg_match( '~^https?://~', $source, $match ) ) {
130 $media = $source;
131 break;
132 }
133 }
134
135 $random = (isset($_POST['hash'])) ? trim( wp_strip_all_tags( sanitize_text_field( $_POST['hash'] ) ) ) : false;
136 if( isset($media) ) {
137 $remotefilename = $media;
138 $remotefilename_encoded = flowplayer::get_encoded_url($remotefilename);
139
140 $bValidFile = true;
141
142 if ( ! class_exists( 'getID3' ) ) {
143 require( ABSPATH . WPINC . '/ID3/getid3.php' );
144 }
145 $getID3 = new getID3;
146
147 $upload_dir = wp_upload_dir();
148 $localtempfilename = trailingslashit( $upload_dir['basedir'] ).'fv_flowlayer_tmp_'.md5(wp_rand(1,999)).'_'.basename( substr($remotefilename_encoded,0,32) );
149
150 global $wp_filesystem;
151
152 if ( is_null( $wp_filesystem ) ) {
153 require_once ABSPATH . '/wp-admin/includes/file.php';
154 WP_Filesystem();
155 }
156
157 $real_file_size = 0;
158 $analysis_size = 8 * 1024 * 1024;
159
160 $remotefilename_encoded = $fv_fp->get_video_src( $remotefilename_encoded, array( 'dynamic' => true ) );
161
162 $res = wp_remote_get( $remotefilename_encoded, array(
163 'headers' => array(
164 'range' => 'bytes=0-' . $analysis_size,
165 'referer' => home_url()
166 ),
167 'timeout' => !$this->is_cron && !$force_is_cron ? apply_filters( 'fv_flowplayer_checker_timeout_quick', 2 ) : 20,
168 'user-agent' => 'FV Player video checker/' . $fv_wp_flowplayer_ver
169 ) );
170
171 if ( ! is_wp_error( $res ) ) {
172
173 // Get the real file size out of Content-length: 0-8388608/{full file size here}
174 if ( $content_range = wp_remote_retrieve_header( $res, 'content-range' ) ) {
175 $content_range_parts = explode( '/', $content_range );
176 if ( 2 === count( $content_range_parts ) ) {
177 $real_file_size = intval( $content_range_parts[1] );
178 }
179 }
180
181 $wp_filesystem->put_contents( $localtempfilename, wp_remote_retrieve_body( $res ) );
182
183 if( !empty($res['response']['code']) ) {
184 $code = intval($res['response']['code']);
185 if( $code == 404 ) {
186 $error = 'Video not found';
187
188 } else if( $code == 403 ) {
189 $error = 'Access denied';
190
191 } else if( $code > 399 ) {
192 $error = 'HTTP '.$code;
193 if( !empty($res['response']['message']) ) {
194 $error .= ': '.$res['response']['message'];
195 }
196 }
197 }
198
199 list( $aVideoErrors, $sContentType, $bFatal ) = $this->check_headers( $res, $remotefilename, $random );
200 if( $bFatal ) {
201 $bValidFile = false;
202 }
203
204 if( $bValidFile ) {
205 $ThisFileInfo = $getID3->analyze( $localtempfilename );
206 }
207
208 $wp_filesystem->delete( $localtempfilename );
209
210 } else {
211 $bValidFile = false;
212 }
213
214 /*
215 Only check file length
216 */
217
218 if( (isset($meta_action) && $meta_action == 'check_time') || $force_is_cron ) {
219 $time = false;
220 $width = false;
221 $height = false;
222
223 if( isset($ThisFileInfo) && isset($ThisFileInfo['playtime_seconds']) ) {
224 $time = $ThisFileInfo['playtime_seconds'];
225
226 /**
227 * MP3 duration check is not reliable if we only check a part of file.
228 * So here we multiply the duration by the ratio of the real file size to the analysis size,
229 * if it's bigger than the analysis size.
230 */
231 if ( stripos( $remotefilename_encoded, '.mp3' ) !== false ) {
232 if ( $real_file_size > $analysis_size ) {
233 $time = $time * $real_file_size / $analysis_size;
234 }
235 }
236 }
237 if( !empty($ThisFileInfo['video']['resolution_x']) ) {
238 $width = intval($ThisFileInfo['video']['resolution_x']);
239 }
240 if( !empty($ThisFileInfo['video']['resolution_y']) ) {
241 $height = intval($ThisFileInfo['video']['resolution_y']);
242 }
243
244 $is_live = false;
245 $is_encrypted = false;
246
247 if(preg_match('/.m3u8(\?.*)?$/i', $remotefilename_encoded)){
248 $remotefilename_encoded = apply_filters( 'fv_flowplayer_video_src', $remotefilename_encoded , array('dynamic'=>true) );
249 $request = wp_remote_get(
250 $remotefilename_encoded,
251 array(
252 'headers' => array(
253 'referer' => home_url(),
254 ),
255 'timeout' => 15,
256 'user-agent' => 'FV Player video checker/' . $fv_wp_flowplayer_ver
257 )
258 );
259
260 $response_code = wp_remote_retrieve_response_code( $request );
261 if( $response_code == 404 ) {
262 return array( 'error' => 'Video not found' );
263
264 } else if( $response_code == 403 ) {
265 return array( 'error' => 'Access denied' );
266
267 } else if( is_wp_error($request) ) {
268 return array( 'error' => $request->get_error_message() );
269 }
270
271 $response = wp_remote_retrieve_body( $request );
272
273 $playlist = false;
274 $duration = 0;
275 $segments = false;
276
277 if(preg_match_all('/^#EXTINF:([0-9]+\.?[0-9]*)/im', $response,$segments)){
278 $is_live = stripos( $response, '#EXT-X-ENDLIST' ) === false;
279
280 foreach($segments[1] as $segment_item){
281 $duration += $segment_item;
282 }
283 } else {
284 $lines = explode( "\n", $response );
285
286 $streams = array();
287 $had_ext_x_stream_inf = false;
288 $resoluton_x_max = 0;
289 $resoluton_y_max = 0;
290
291 foreach( $lines AS $line ) {
292 // last line was starting with #EXT-X-STREAM-INF:
293 if( stripos($line,'#') !== 0 && $had_ext_x_stream_inf ) {
294 $streams[] = trim($line);
295 $had_ext_x_stream_inf = false;
296 }
297
298 if( stripos($line,'#EXT-X-STREAM-INF:') === 0 ) {
299 $had_ext_x_stream_inf = true;
300
301 if( stripos($line,'RESOLUTION=') !== false ) {
302 if( preg_match( '~RESOLUTION=(\d+)x(\d+)~', $line, $resoluton ) ) {
303 if( $resoluton[1] > $resoluton_x_max ) {
304 $resoluton_x_max = $resoluton[1];
305 }
306 if( $resoluton[2] > $resoluton_y_max ) {
307 $resoluton_y_max = $resoluton[2];
308 }
309 }
310 }
311 }
312
313 }
314
315 $width = $resoluton_x_max;
316 $height = $resoluton_y_max;
317
318 foreach($streams as $item){
319 $item_url = $item;
320
321 // If the stream URL is relative, we need to take the master playlist URL, remove the filename part and put in the stream link
322 if( stripos( $item_url, 'http://' ) !== 0 && stripos( $item_url, 'https://' ) !== 0 ) {
323 $item_url = preg_replace('/[^\/]*\.m3u8(\?.*)?/i', $item, $remotefilename_encoded);
324 }
325 if( $secured_url = $fv_fp->get_video_src( $item_url, array( 'dynamic' => true ) ) ) {
326 $item_url = $secured_url;
327 }
328
329 $request = wp_remote_get(
330 $item_url,
331 array(
332 'headers' => array(
333 'referer' => home_url()
334 ),
335 'user-agent' => 'FV Player video checker/' . $fv_wp_flowplayer_ver
336 )
337 );
338
339 if( is_wp_error($request) ) {
340 return array( 'error' => $request->get_error_message() );
341 }
342
343 $playlist_item = wp_remote_retrieve_body( $request );
344
345 if(preg_match_all('/^#EXTINF:([0-9]+\.?[0-9]*)/im', $playlist_item,$segments)){
346 $is_live = stripos( $playlist_item, '#EXT-X-ENDLIST' ) === false;
347 $is_encrypted = stripos( $playlist_item, '#EXT-X-KEY' ) !== false;
348
349 foreach($segments[1] as $segment_item){
350 $duration += $segment_item;
351 }
352 }
353 if($duration > 0)
354 break;
355 }
356 }
357
358 $time = $duration;
359 }
360
361 $time = apply_filters( 'fv_flowplayer_checker_time', $time, $remotefilename_encoded );
362 $key = flowplayer::get_video_key($remotefilename_encoded);
363
364 global $post;
365 $fv_flowplayer_meta = array();
366 if( !empty($post) ) {
367 $fv_flowplayer_meta = get_post_meta( $post->ID, $key, true );
368 if( !$fv_flowplayer_meta ) $fv_flowplayer_meta = array();
369 }
370
371 $fv_flowplayer_meta['error'] = $error;
372 $fv_flowplayer_meta['duration'] = $time;
373 $fv_flowplayer_meta['width'] = $width;
374 $fv_flowplayer_meta['height'] = $height;
375 $fv_flowplayer_meta['is_live'] = $is_live;
376 $fv_flowplayer_meta['is_encrypted'] = $is_encrypted;
377 $fv_flowplayer_meta['etag'] = ! is_wp_error( $res ) && isset($res['headers']['etag']) ? $res['headers']['etag'] : false; // todo: check!
378 $fv_flowplayer_meta['date'] = time();
379 $fv_flowplayer_meta['check_time'] = microtime(true) - $tStart;
380
381 if( $time > 0 || $error || $this->is_cron ) {
382 if( !empty($post) ) {
383 update_post_meta( $post->ID, $key, $fv_flowplayer_meta );
384 }
385 return $fv_flowplayer_meta;
386 }
387
388 }
389
390 } // end isset($media)
391 }
392 }
393
394
395
396
397 function checker_cron() {
398 global $fv_fp;
399 if( $fv_fp->_get_option('video_model_db_checked') && $fv_fp->_get_option('video_meta_model_db_checked') ) {
400
401 // get all video IDs for which the duration is zero and are not live streams and were not checked in last day
402 global $wpdb;
403 $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" );
404
405 if( $aVideos ) {
406 global $FV_Player_Db;
407 foreach( $aVideos AS $video_id ) {
408 $objVideo = new FV_Player_Db_Video( $video_id, array(), $FV_Player_Db );
409
410 $last_check = $objVideo->getLastCheck();
411
412 $check_ttl = 86400;
413
414 $is_live = $objVideo->getLive();
415 if( $is_live && FV_Player_YouTube()->is_youtube( $objVideo->getSrc() ) ) {
416 $check_ttl = 300;
417 }
418
419 if( $last_check && intval($last_check) + $check_ttl > time() ) {
420 continue;
421 }
422
423 $error_count = $objVideo->getMetaValue('error_count',true);
424
425 if( $error_count && intval($error_count) > 5 ) {
426 continue;
427 }
428
429 // Make sure we do no loose video meta!
430 $objVideo->save( array(), true );
431
432 }
433 }
434 }
435
436 // legacy
437 if( !$aQueue = self::queue_get() ) return;
438 $tStart = microtime(true);
439 $this->is_cron = true;
440 foreach( $aQueue AS $key => $item ) {
441 if( microtime(true) - $tStart > apply_filters( 'fv_flowplayer_checker_cron_time', 20 ) ) {
442 break;
443 }
444 global $post;
445 $tmp = $post;
446 $post = get_post($key);
447
448 do_action( 'fv_flowplayer_checker_cron_post', $key );
449
450 fv_wp_flowplayer_save_post($key);
451 $post = $tmp;
452 }
453
454 }
455
456
457
458
459 function cron_init() {
460 if ( !wp_next_scheduled( 'fv_flowplayer_checker_event' ) ) {
461 wp_schedule_event( time(), '5minutes', 'fv_flowplayer_checker_event' );
462 }
463 }
464
465
466
467
468 function cron_schedules( $schedules ) {
469 $schedules['5minutes'] = array(
470 'interval' => 300,
471 'display' => __('Every 5 minutes')
472 );
473 return $schedules;
474 }
475
476
477
478
479 public static function get_videos( $post_id ) {
480 global $fv_fp;
481
482 $objPost = get_post($post_id);
483 if( $objPost ) {
484 $content = $objPost->post_content;
485 preg_match_all( '~\[(?:flowplayer|fvplayer).*?\]~', $content, $matches );
486
487 $aMeta = get_post_custom($post_id);
488 if( $aMeta && is_array($aMeta) && count($aMeta) > 0) {
489 $meta_values = '';
490 foreach( $aMeta AS $values ) {
491 $meta_values .= implode("", $values);
492 }
493 if( preg_match_all( '~\[(?:flowplayer|fvplayer).*?\]~', $meta_values, $meta_matches ) ) {
494 $matches[0] = array_merge($matches[0], $meta_matches[0]);
495 }
496 }
497
498 }
499
500 $videos = array();
501 if( isset($matches[0]) && count($matches[0]) ) {
502 $aPlaylistItems = array();
503 foreach( $matches[0] AS $shortcode ) {
504 $aArgs = shortcode_parse_atts( rtrim($shortcode,']') );
505 list( $playlist_items_external_html, $aPlaylistItems ) = $fv_fp->build_playlist( $aArgs, isset($aArgs['src']) ? $aArgs['src'] : false, false, false, false, false, true );
506
507 if( count($aPlaylistItems) > 0 ) {
508 foreach( $aPlaylistItems AS $aItem ) {
509 if( isset($aItem['sources']) && isset($aItem['sources'][0]) && isset($aItem['sources'][0]['src']) ) {
510 $videos[] = $aItem['sources'][0]['src'];
511 }
512 }
513 }
514 }
515 }
516
517 if( count($videos) > 0 ) {
518 $videos = array_unique($videos);
519 } else {
520 $videos = false;
521 }
522 return $videos;
523 }
524
525
526
527
528 public static function queue_add( $post_id ) {
529 $aQueue = get_option( 'fv_flowplayer_checker_queue' ) ? get_option( 'fv_flowplayer_checker_queue' ) : array();
530 $aQueue[$post_id] = true;
531 update_option( 'fv_flowplayer_checker_queue', $aQueue );
532 }
533
534
535
536
537 public static function queue_check( $post_id = false ) {
538 global $post;
539 $post_id = ( isset($post->ID) ) ? $post->ID : $post_id;
540 $aQueue = get_option( 'fv_flowplayer_checker_queue' ) ? get_option( 'fv_flowplayer_checker_queue' ) : array();
541 if( in_array($post_id,array_keys($aQueue)) ) {
542 return true;
543 }
544 return false;
545 }
546
547
548
549
550 public static function queue_get() {
551 return get_option( 'fv_flowplayer_checker_queue', array() );
552 }
553
554
555
556
557 public static function queue_remove( $post_id ) {
558 $aQueue = get_option( 'fv_flowplayer_checker_queue' ) ? get_option( 'fv_flowplayer_checker_queue' ) : array();
559 if( isset($aQueue[$post_id]) ) {
560 unset($aQueue[$post_id]);
561 }
562 update_option( 'fv_flowplayer_checker_queue', $aQueue );
563 }
564
565
566
567
568 }
569