Отображать (и управлять) изображениями веб-камеры?

У меня есть веб-камера, которая пишет каждую минуту или около того, изображение в папку ftp на сервере, где установлен WP. Мой вопрос заключается в том, как получить последнее изображение, отображаемое на странице, с обновлением каждые 60 секунд и удалить старые файлы в этой папке.

Хорошо, вот код для того, чтобы страницы WordPress перерисовывали изображение на странице по мере обнаружения новых изображений (например, для каталога, в который веб-камера автоматически загружает изображения). Он предполагает следующее:

  1. Вы используете PHP 5.3 или выше (легко модифицировать, чтобы не требовать этого)
  2. У вас есть ваши изображения веб-камеры, загружаемые где-то прямо через Интернет
  3. Вы не хотите размещать несколько изображений с камеры на одной странице или в сообщении

Я уверен, что у него много возможностей для улучшения, но он делает следующее:

  • Позволяет настроить различные страницы камеры

Раздел 1 является основным файлом плагина. Он просто должен быть помещен в php-файл, учитывая заголовок плагина и помещен в корневую папку соответствующей плагины.

Раздел 2 – это файл javascript. Основной файл плагина предполагает, что он находится в подкаталоге с именем js и имеет имя файла web_cam_checker.js .

Раздел 1: Файл PHP

 /* Plugin Name: Whatever You Want Here Description: Handles updating an image via the heartbeat api for a web cam - in answer to a question on stack exchange Version: 0.0.1 Author: The Privateer */ namespace Privateer\WebCam; try { if ( class_exists('Privateer_Do_Web_Cam_Updates') ) { throw new \Exception('Privateer_Do_Web_Cam_Updates already exists!', 10001); } else { class Privateer_Do_Web_Cam_Updates{ protected $images_dir; # file system url images are uploaded to protected $images_url; # web uri images are available at protected $image_tag_id; # id of image to be swapped out on displayed page protected $do_purge_images; # should old files be deleted protected $refresh_interval_s; # how often the cam should refresh protected $is_debug; # boolean - use debug mode? protected $init_retry_ms; # Time in seconds to wait for initialization each try protected $min_init_retries; # Maximum number of attempts to wait for initialization before quitting protected $notices; # Any notices issued protected $errors; # Any errors function __construct( $image_tag_id = '', $images_dir = '', $images_url = '', $refresh_interval_s = 0, $init_retry_ms = 0, $min_init_retries = 0, $is_debug = false, $do_purge_images = false ) { $this->notices = array(); $defaults = $this->get_default_settings(); $this->images_dir = ( empty($images_dir) )? $defaults['images_dir'] : (string) $images_dir; $this->validate_images_dir_or_throw(); $images_url = ( empty($images_url) )? $defaults['images_url'] : (string) $images_url; if ( empty( $images_url ) ) { throw new \Exception("URL [{$images_url}] not found. Use _privateer_web_cam_images_url filter to set properly.", 10001); } else { $this->images_url = $images_url; } $image_tag_id = ( empty($image_tag_id) ) ? $defaults['image_tag_id'] : (string) $image_tag_id; if ( empty($image_tag_id) ) { throw new \Exception("Image Tag ID empty. Please fix via _privateer_web_cam_image_tag_id filter.", 10001); } else { $this->image_tag_id = $image_tag_id; } $do_purge_images = ( empty($do_purge_images) ) ? $defaults['purge_old_images'] : (bool) $do_purge_images; $this->do_purge_images = ( $do_purge_images === true )? true : false; # Limitations imposed by wp.heartbeat $refresh_interval_s = ( empty( $refresh_interval_s ) )? $defaults['refresh_interval_seconds'] : (int) $refresh_interval_s; if ( 5 > $refresh_interval_s ) { $this->notices[] = "Min Refresh Interval is 5 seconds. Adjusted from {$refresh_interval_s} to 5."; $this->refresh_interval_s = 5; } else if ( 120 < $refresh_interval_s ) { $this->notices[] = "Max Refresh Interval is 120 seconds. Adjusted from {$refresh_interval_s} to 120."; $this->refresh_interval_s = 120; } else { $this->refresh_interval_s = $refresh_interval_s; } $is_debug = ( is_null($is_debug) )? $defaults['debug'] : (bool) $is_debug; $this->is_debug = ( $is_debug )? 1 : 0; $init_retry_ms = ( empty( $init_retry_ms ) )? $defaults['init_retry_ms'] : (int) $init_retry_ms; if ( 200 > $init_retry_ms ) { $this->notices[] = "Init Retry Time mimimum is 200 milliseconds. Adjusted from {$init_retry_ms} to 200."; $this->init_retry_ms = 200; } else { $this->init_retry_ms = $init_retry_ms; } $min_init_retries = ( empty( $min_init_retries ) )? $defaults['init_min_retries'] : (int) $min_init_retries; if ( 1 > $min_init_retries ) { $this->notices[] = "Min Init Retries is 1. Adjusted from {$min_init_retries} to 1."; $this->min_init_retries = 1; } else { $this->min_init_retries = $min_init_retries; } } protected function get_default_settings() { return array( 'images_dir' => plugin_dir_path( __FILE__ ) . 'cam-images', 'images_url' => plugin_dir_url( __FILE__ ) . 'cam-images', 'image_tag_id' => 'main_cam_image', 'purge_old_images' => false, 'refresh_interval_seconds' => 30, 'debug' => WP_DEBUG, 'init_retry_ms' => 500, 'init_min_retries' => 10 ); } protected function validate_images_dir_or_throw() { if ( !is_dir( $this->images_dir ) ) { throw new \Exception("Directory [{$this->images_dir}] not found. Use _privateer_web_cam_images_dir filter to set properly.", 10001); } else if ( !is_readable( $this->images_dir) ) { throw new \Exception("Directory [{$this->images_dir}] not readable.", 10001); } } # The function that processes received heartbeats via ajax # - response: what we will be sending back (filtered) # - data: what we received # - screen_id: will be 'front' or an admin page # Anything returning an error key will tell the javascript to stop public function do_process_heartbeat_received( $response, $data, $screen_id ) { $r = array(); $key = 'web_cam_checker_' . $this->image_tag_id; if ( 'front' !== "{$screen_id}" ) { $r['error'] = 'Not on front end of site.'; } else if ( !array_key_exists($key, $data) ) { $r['error'] = "Failed to locate key {$key} in data received"; } else if ( !array_key_exists('current_image_src', $data["{$key}"]) ) { $r['error'] = "Did not find current_image_src in {$key} data"; } else { $current = $this->get_current_web_cam_image(); $reported = (string) $data["{$key}"]['current_image_src']; if ( "{$current}" == "{$reported}" ) { $r['notice'] = 'Image has not changed'; } else { $r['webcam_new_uri'] = "{$current}"; } } $response["{$key}"] = $r; return $response; } protected function get_readable_images_in_image_dir() { $this->validate_images_dir_or_throw(); $images = array(); if ( $handle = opendir( "{$this->images_dir}" ) ) { while ( false !== ( $file_name = readdir( $handle ) ) ) { switch ( "{$file_name}" ) { case '.': case '..': # Skip current and previous directory links break; default: # Build the full file path to the file found $file_path = "{$this->images_dir}/{$file_name}"; if ( is_file( "{$file_path}" ) && is_readable( "{$file_path}" ) ) { # TODO: Check to be sure it is an image $images["{$file_name}"] = $file_path; } break; } } @closedir( $handle ); } else { $this->notices[] = "Failed to open directory {$this->images_dir} for reading."; } return $images; } protected function get_newest_image_name($images) { $newest_name = ''; $newest_ts = 0; foreach ( $images as $name => $path ) { $last_modified = filectime( $path ); if ( $last_modified > $newest_ts ) { $newest_name = $name; $newest_ts = $last_modified; } } return $newest_name; } protected function get_current_web_cam_image() { $current = ''; # The newest image on the web server try { $this->validate_images_dir_or_throw(); $images = $this->get_readable_images_in_image_dir(); if ( 0 < count($images) ) { $newest_name = $this->get_newest_image_name($images); $current = "{$this->images_url}/{$newest_name}"; if ( $this->do_purge_images ) { $this->purge_older_images($images, $newest_name); } } } catch ( \Exception $e ) { $this->append_exception( $e ); $code = $e->getCode(); $message = $e->getMessage(); $trace = $e->getTraceAsString(); $line = $e->getLine(); $file = $e->getFile(); $err = new \WP_Error( "Error: {$file}(line {$line}): {$code} {$message}", $trace ); # You can hook into this to log errors somewhere if wanted do_action('_privateer_do_web_cam_updates_error', $err); } return $current; } protected function append_exception( \Exception $e ) { $code = $e->getCode(); $message = $e->getMessage(); $trace = $e->getTraceAsString(); $line = $e->getLine(); $file = $e->getFile(); $this->errors[] = new \WP_Error( "Error: {$file}(line {$line}): {$code} {$message}", $trace ); } protected function purge_older_images( $images, $newest_image ) { foreach ( $images as $file_name => $to_remove ) { if ( "{$file_name}" !== "{$newest_image}" ) { if( is_file( "{$to_remove}" ) && is_writeable( "{$to_remove}" ) ) { if ( $this->is_debug ) { $this->notices[] = "Would now be removing {$to_remove}"; } else { $removed = unlink( "{$to_remove}" ); if ( !$removed ) { $this->notices[] = "Failed to remove image: {$to_remove}"; } } } } } } # Use the _privateer_web_cam_loading filter to get the script to load where wanted public function do_setup_javascript() { $do_js = apply_filters('_privateer_web_cam_loading', false, $this->image_tag_id); if ( $do_js ) { add_action('get_header', array($this, 'do_register_js') ); add_action('wp_head', array($this, 'do_enqueue_js')); } } public function do_register_js() { wp_register_script('privateer_web_cam', plugins_url( '/js/web_cam_checker.js', __FILE__ ), array( 'jquery', 'heartbeat' ), "0.0.2", true); } public function do_enqueue_js() { $web_cam_config = array( 'image_id' => "{$this->image_tag_id}", 'refresh_interval' => (int)$this->refresh_interval_s, 'debug' => (int) $this->is_debug, 'init_retry_ms' => $this->init_retry_ms, 'min_init_retries' => $this->min_init_retries ); wp_localize_script('privateer_web_cam', 'pri_web_cam_settings', $web_cam_config ); wp_enqueue_script('privateer_web_cam'); } function __destruct() { do_action('_privateer_web_cam_runtime_errors', $this->errors); do_action('_privateer_web_cam_runtime_notices', $this->notices); } } function do_choose_privateer_web_cam_where_to_load($load, $image_id) { if ( 'main_cam_image' == "{$image_id}" && is_front_page() ) { $load = true; } return $load; } add_filter( '_privateer_web_cam_loading', '\\Privateer\\WebCam\\do_choose_privateer_web_cam_where_to_load', 10, 2); # Create up an object to handle the web cam and provide wanted defaults # Do this multiple times if you will be using different cam directories and/or image tags $o_privateer_web_cam = new Privateer_Do_Web_Cam_Updates( 'main_cam_image', '', '', 0, 0, 0, true, false ); if ( is_a( $o_privateer_web_cam, '\Privateer\WebCam\Privateer_Do_Web_Cam_Updates' ) ) { # Set up the ajax responses add_filter( 'heartbeat_received', array($o_privateer_web_cam, 'do_process_heartbeat_received'), 10, 3 ); add_filter( 'heartbeat_nopriv_received', array($o_privateer_web_cam, 'do_process_heartbeat_received'), 10, 3 ); # Set up the javascript for the front end on templates that you want it used on if ( !is_admin() ) { add_action( 'get_header', array($o_privateer_web_cam, 'do_setup_javascript'), 9 ); } } else { throw new \Exception('Failed to create Privateer_Do_Web_Cam_Updates object', 10001); } } } catch ( \Exception $e ) { $code = $e->getCode(); $message = $e->getMessage(); $trace = $e->getTraceAsString(); $line = $e->getLine(); $file = $e->getFile(); $err = new \WP_Error( "Error: {$file}(line {$line}): {$code} {$message}", $trace ); do_action('_privateer_web_cam_init_errors', $err); if ( WP_DEBUG ) { wp_die( "Error in {$file} on line {$line}: Code:{$code}, Message: {$message}, Trace: {$trace}" ); } } 

Параметры конструктора :

  • image_tag_id: значение в атрибуте id изображения для отображения через
  • images_dir: полный путь к каталогу на сервере, на котором хранятся изображения для этой камеры
  • images_url: Образцы изображений с достижением мира можно просмотреть по адресу
  • refresh_interval_s: секунды между биениями для этой камеры
  • init_retry_ms: во время init сколько миллисекунд ждать между попытками
  • min_init_retries: минимальные попытки сделать при инициализации, прежде чем сдаться
  • is_debug: показывать или не выводить отладочные сообщения в консоли js
  • do_purge_images: удалять старые изображения в images_dir

Примечание: images_dir и images_url предполагают, что вы создали cam-изображения в корневой директории плагина. Вы можете настроить их так, как хотите.

Выбор того, какие страницы нужны javascript для камеры

Я решил по умолчанию не загружать javascript вообще и использовать фильтр, чтобы пользователь мог выбрать, для каких страниц нужен скрипт.

 function do_choose_privateer_web_cam_where_to_load($load, $image_id) { if ( 'main_cam_image' == "{$image_id}" && is_front_page() ) { $load = true; } else if ( 'second_cam_image' == "{$image_id}" && is_page('cam_two') ) { $load = true; } return $load; } add_filter( '_privateer_web_cam_loading', '\\Privateer\\WebCam\\do_choose_privateer_web_cam_where_to_load', 10, 2); 

Обратите внимание, что я изменил это, чтобы предположить, что загружаются два объекта кулачка:

  1. Один имеет id = "main_cam_image" на первой странице
  2. Другой, имеющий id = "second_cam_image" на странице с slug 'cam_two'

Tweak по мере необходимости с различными функциями is_ * в wordpress, чтобы загрузить файл javscript, где нужно.

Помимо этого, главная настройка сразу же следует за ней.

Если вы хотите создать объект кулачка в другом месте (скажем, файл functions.php), вы захотите использовать пространство имен (например, следующее):

 $o_cam_two = new \Privateer\WebCam\Privateer_Do_Web_Cam_Updates( 'cam_two', '', '', 0, 0, 0, true, false ); if ( is_a( $o_cam_two, '\Privateer\WebCam\Privateer_Do_Web_Cam_Updates' ) ) { # Set up the ajax responses add_filter( 'heartbeat_received', array($o_cam_two, 'do_process_heartbeat_received'), 10, 3 ); add_filter( 'heartbeat_nopriv_received', array($o_cam_two, 'do_process_heartbeat_received'), 10, 3 ); # Set up the javascript for the front end on templates that you want it used on if ( !is_admin() ) { add_action( 'get_header', array($o_cam_two, 'do_setup_javascript'), 9 ); } } 

Раздел второй: файл Javascript. Все советы будут очень благодарны тем, кто знаком с javascript. Я просто изучаю это, но сделал все, что мог. Это работает … и это что-то.

 jQuery(document).ready(function($) { (function( document, config ) { var settings = { $cam_image: null, cam_data: { current_image_src: null }, image_id: config.image_id, debug: parseInt( config.debug ), document: document, tick_interval: parseInt( config.refresh_interval ), waited: 0, max_wait: parseInt( config.init_retry_ms ), wait_delay_s: parseInt( config.min_init_retries ) }; function do_trigger(type, caller, problem ) { console.log('Triggering ' + type + ', Caller: ' + caller + ', Problem: ' + problem); if ( 'warning' === type ) { settings.$document.trigger('web-cam-warning', caller + ': ' + problem); } else { settings.$document.trigger('web-cam-error', caller + ': ' + problem); } } function do_enqueue_image(data) { console.log('Trying to enqueue image...'); if ( ! wp.heartbeat.enqueue('web_cam_checker_' + settings.image_id, data, true ) ) { do_trigger('error', 'do_enqueue_image', 'Failed to add to wp.heartbeat.enqueue. Data: ' + JSON.stringify( data )); } else if ( settings.debug ) { console.log( 'Queued: ' + JSON.stringify( wp.heartbeat.getQueuedItem('web_cam_checker_' + settings.image_id) ) ); } } function do_process_response(el, data) { if ( settings.debug ) { console.log( 'process_response:' ); console.log( '######\n ' + 'el: ' + JSON.stringify(el) + '\n######' ); console.log( '######\n ' + 'data: ' + JSON.stringify(el) + '\n######' ); } if ( data['webcam_new_uri'] ) { if ( settings.debug ) { console.log('Found webcam_new_uri: ' + data['webcam_new_uri']); } settings.cam_data.current_image_src = data['webcam_new_uri'] + ''; settings.$cam_image.prop('src', settings.cam_data.current_image_src); var worked = do_swap_current_image(); if ( worked ) { if ( settings.debug ) { console.log( 'Swam image worked, setting up next heartbeat queue.' ); } do_enqueue_image(settings.cam_data); } } else { if ( data['notice'] ) { if ( settings.debug ) { console.log('Notice Received: ' + data['notice'] + '\nSetting up next heartbeat queue.'); } do_enqueue_image(settings.cam_data); } else if ( data['error'] ) { do_trigger('error', 'do_process_response', data['error']); } if ( settings.debug ) { console.log('Full Data: ' + JSON.stringify(data) ); } } } function do_swap_current_image() { var worked = false; if ( settings.debug ) { console.log('attempting image swap'); } var updated_src = settings.cam_data.current_image_src; $("<img/>") .one('load', function() { if ( settings.debug ) { console.log('Finished updating to ' + $(this).prop('src')); } worked = true; }) .prop('src', updated_src ) .each(function(){ if ( this.complete ) { $(this).trigger('load'); } else { //do_trigger('error', 'do_swap_current_image', 'Did not finish updating to ' + $(this).prop('src')); worked = true } }); return worked; } function do_setup_timeout( waiting_on ) { settings.waited += 1; if ( settings.waited < settings.max_wait ) { setTimeout( do_init(), settings.wait_delay_s * 1000 ); } else { do_trigger('error', 'do_setup_timeout', 'Giving up on ' + waiting_on + ' (waited ' + settings.waited + ' times)'); } } function do_init() { if ( typeof window.wp === 'undefined' ) { do_setup_timeout('window.wp'); } else if ( typeof window.wp.heartbeat === 'undefined' ) { do_setup_timeout('window.wp.heartbeat'); } else if ( typeof settings.image_id === 'undefined' ) { do_trigger('error', 'do_init', 'Cannot start web cam without html image tag id name'); } else { settings.$cam_image = $('#' + settings.image_id); console.log('Settings:' + JSON.stringify(settings.$cam_image)); if ( 0 === settings.$cam_image.length ) { do_trigger('error', 'do_init', 'Failed to locate image #' + settings.image_id); } else { if ( settings.interval < 5 ) { do_trigger('warning', 'do_init', 'Interval cannot be shorter than 5 seconds. Detected as ' + settings.interval ); settings.interval = 5; } else if ( settings.interval > 120 ) { do_trigger('warning', 'do_init', 'Interval cannot be longer that 120 seconds. Detected as ' + settings.interval ); settings.interval = 120; } settings.cam_data.current_image_src = settings.$cam_image.prop('src'); console.log('Settings Now: ' + JSON.stringify( settings )); do_enqueue_image( settings.cam_data ); document.on('heartbeat-send', function(el, data) { if ( settings.debug ) { console.log('Data sent was ' + JSON.stringify( data )); } }).on('heartbeat-tick.web_cam_checker_' + settings.image_id, function(el, data) { console.log('detected heartbeat tick:' + JSON.stringify(el)); if ( data.hasOwnProperty('web_cam_checker_' + settings.image_id) ) { if ( settings.debug ) { console.log('Data has web_cam_checker_' + settings.image_id); } do_process_response(el, data['web_cam_checker_' + settings.image_id]); } else if ( settings.debug ) { console.log('Data lacks web_cam_checker_' + settings.image_id + ': ' + JSON.stringify(data) ); } }); wp.heartbeat.interval(settings.tick_interval); } } } do_init(); })( $(document), pri_web_cam_settings ); $(document) .on('web-cam-error', function(e) { console.log('Web Cam Error: ' + e); }) .on('web-cam-warning', function(e) { console.log('Web Cam Warning: ' + e); }) .on('heartbeat.error', function(e) { console.log('Heartbeat Error: ' + JSON.stringify(e) ); }); }); 

Спасибо за идею Кайзера. Я не слышал об API сердечного ритма, и я искал что-то, что можно было бы попробовать, увеличив знания javascript … так что это было хорошее упражнение.

Я только пробовал это на LAMP-сервере, просматривая Firefox … и нет, я еще не поставил строгую декларацию … но могу сделать это в следующий раз, когда у меня будет время.

В любом случае, надеюсь, это немного поможет кому-то.

Любой для тех, кто новичок в коде …

Чтобы это работало так:

  1. Создайте каталог в своем каталоге wp-content / plugins (назовите его как угодно, я буду использовать обновления для веб-камеры)
  2. Создайте новый текстовый файл с именем privateer-web-cam-updates.php в этом каталоге и откройте его в текстовом редакторе
  3. Добавьте id = "main_cam_image" в тег изображения на вашем сайте (например, на первой странице). Это может выглядеть как <img src="#" id="main_cam_image" title="My Web Cam" />
  4. Измените следующий раздел кода в плагине.

function do_choose_privateer_web_cam_where_to_load($load, $image_id) { if ( 'main_cam_image' == "{$image_id}" && is_front_page() ) { $load = true; } return $load; } add_filter( '_privateer_web_cam_loading', '\\Privateer\\WebCam\\do_choose_privateer_web_cam_where_to_load', 10, 2);

Если у вас есть изображение в блоге, измените is_front_page() на is_home() . Если у вас есть это на странице, получите идентификатор страницы, а затем измените is_front_page() на is_page(n) где n – это идентификатор страницы.

  1. Создайте подкаталог Cam-images в папке плагина и установите веб-камеру для размещения там изображений.
  2. Создайте подкаталог js в папке плагина.
  3. Скопируйте код из раздела 2 в новый текстовый файл с именем web_cam_checker.js

И снова, конструктивная критика всегда ценится. (по-видимому, не может быть, чтобы последний раздел был определен как код по какой-то причине)