web-dev-qa-db-ja.com

ウェブカメラの画像を表示(および管理)しますか?

私は毎分かそこら、WPがインストールされているサーバー上のftpフォルダに画像を書き込むウェブカメラを持っています。私の質問は、60秒ごとに更新して、ページに表示される最後の画像を取得し、そのフォルダ内の古い画像を削除する方法です。

4

新しい画像が検出されたときにワードプレスのページに画像を再描画させるためのコードです(Webカメラが自動的に画像をアップロードするディレクトリなど)。次のことを前提としています。

  1. あなたはPHP 5.3以上を使っています(それを必要としないように簡単に修正されています)
  2. どこかにアップロードしたWebカメラの画像をWeb経由で直接アクセスできます
  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:世界に到達可能なURL画像は、
  • refresh_interval_s:このカムのハートビート間の秒数
  • init_retry_ms:init中に、再試行間に待機するミリ秒数
  • min_init_retries:最小値は、あきらめる前に初期化を試みます。
  • is_debug:jsコンソールにデバッグメッセージを表示するかどうか
  • do_purge_images:images_dir内の古い画像を削除するかどうか

注意: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オブジェクトがロードされていると仮定するようにこれを変更したことに注意してください。

  1. フロントページにid = "main_cam_image"を持つもの
  2. スラッグが 'cam_two'のページでid = "second_cam_image"を持つもの

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サーバー上でそれを試しただけでした…そしていいえ、私はまだ厳密な宣言をしていません…しかし次回にはいつかするかもしれません。

とにかく、うまくいけばこれは誰かに少し役立つでしょう。

コーディングが初めての人のためのもの...

これをそのまま機能させるには:

  1. あなたのwp-content/pluginsディレクトリの中にディレクトリを作成してください(私はprivateer-web-cam-updatesを使います)
  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()に変更します。ページ上に持っている場合は、ページIDを取得してからis_front_page()is_page(n)に変更します。ここで、nはページのIDです。

  1. プラグインフォルダにcam-imagesサブディレクトリを作成し、そこに画像を配置するようにWebカメラを設定します。
  2. プラグインフォルダにjsサブディレクトリを作成します。
  3. セクション2のコードをweb_cam_checker.jsという名前の新しいテキストファイルにコピーします。

そして、やはり、建設的な批判は常に認められます。 (何らかの理由で最後のセクションがコードとして検出されるようには思われません)

1
Privateer