我相信它有很大的改进空间,但它可以做到以下几点:允许您设置不同的cam页面。第1节是主插件文件。它只需要放在一个php文件中,给定一个插件头,然后放在相应插件文件夹的根目录中。
第2节是javascript文件。主插件文件假定它位于名为js
并具有文件名web_cam_checker.js
.
Section 1: The PHP File
/*
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}" );
}
}
The Constructor参数:image\\u tag\\u id:要通过以下方式显示的图像的id属性中的值:保存此cam图像的服务器上的目录的完整路径。image\\u url:可以在刷新间隔秒处查看世界可访问的url图像。刷新间隔秒数:此cam的心跳间隔秒数,重试之间等待的毫秒数min\\u init\\u重试:放弃之前在初始化时进行的最小尝试数is\\u debug:是否在js控制台上显示调试消息do\\u purge\\u images:是否删除images\\u dir中的旧图像注意:images\\u dir和images\\u url假定您在插件的根目录下创建了cam图像。不过,你可以将它们设置为任何你想要的。
Choosing which pages need the 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);
请注意,我将此更改为假设正在加载两个cam对象:一个在首页上有id=“main\\u cam\\u image”,另一个在页面上有id=“second\\u cam\\u image”,并带有slug“cam\\u two”,可以根据需要使用wordpress中的各种is\\u*函数进行调整,以便在需要的地方加载javscript文件。
除此之外,主设置紧随其后。
如果要在其他位置创建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 );
}
}
Section Two: The Javascript File对于那些更熟悉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服务器上试用过。。。不,我还没有做出严格的声明。。。但下次我有时间的时候可能会这样做。
无论如何,希望这能帮助一些人摆脱困境。
对于那些新的代码。。。
要使其按原样工作,请执行以下操作:
在您的wp内容/插件目录中创建一个目录(随便叫什么,我将使用privateer网络摄像头更新)
- 创建一个名为privateer网络摄像头更新的新文本文件。php,然后在纯文本编辑器中打开它,将id=“main\\u cam\\u image”添加到站点上的图像标记中(比如首页)。可能看起来像
<img src="#" id="main_cam_image" title="My Web Cam" />
编辑插件中的以下代码部分
在插件文件夹中创建一个cam图像子目录,并将您的网络摄像头设置为将图像放置在那里在插件文件夹中创建js子目录将第2节中的代码复制到名为web\\u cam\\u checker的新文本文件中。再次强调,建设性的批评总是值得赞赏的。(由于某种原因,似乎无法将最后一节检测为代码)