私は毎分かそこら、WPがインストールされているサーバー上のftpフォルダに画像を書き込むウェブカメラを持っています。私の質問は、60秒ごとに更新して、ページに表示される最後の画像を取得し、そのフォルダ内の古い画像を削除する方法です。
新しい画像が検出されたときにワードプレスのページに画像を再描画させるためのコードです(Webカメラが自動的に画像をアップロードするディレクトリなど)。次のことを前提としています。
改善の余地は十分にあると思いますが、次のことが行われます。
セクション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}" );
}
}
コンストラクタ パラメータ:
注意:images_dirとimages_urlは、プラグインのルートディレクトリの下にcam-imagesを作成したと仮定しています。あなたが望むものにそれらを設定することができます。
どのページに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);
2つのcamオブジェクトがロードされていると仮定するようにこれを変更したことに注意してください。
Wordpress内のさまざまなis_ *関数を使用して必要に応じて微調整し、必要な場所にJavaScriptファイルをロードします。
それ以外は、主な設定はすぐにそれに続いています。
他の場所にcamオブジェクトを作成したい場合(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 );
}
}
セクション2: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の知識を増やすことで試してみるべきものを探していました。
私はFirefoxからブラウズしているLAMPサーバー上でそれを試しただけでした…そしていいえ、私はまだ厳密な宣言をしていません…しかし次回にはいつかするかもしれません。
とにかく、うまくいけばこれは誰かに少し役立つでしょう。
コーディングが初めての人のためのもの...
これをそのまま機能させるには:
<img src="#" id="main_cam_image" title="My Web Cam" />
のように見えるかもしれません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()
に変更します。ページ上に持っている場合は、ページIDを取得してからis_front_page()
をis_page(n)
に変更します。ここで、nはページのIDです。
そして、やはり、建設的な批判は常に認められます。 (何らかの理由で最後のセクションがコードとして検出されるようには思われません)