Exibe (e gerencia) imagens de webcam?

4

Eu tenho uma webcam que escreve a cada minuto, uma imagem para uma pasta de ftp no servidor onde o WP está instalado. Minha pergunta é como obter a última imagem exibida na página, com atualização a cada 60 segundos, e excluir os mais antigos nessa pasta.

    
por Valentino Dell'Aica 18.09.2014 / 09:51

1 resposta

1

Tudo bem, aqui está o código para que as páginas do wordpress redesenhem uma imagem em uma página à medida que novas imagens são detectadas (como para um diretório para o qual uma web cam automaticamente envia imagens). Assume o seguinte:

  1. Você está usando o PHP 5.3 ou superior (facilmente modificado para não exigir isso)
  2. Você tem imagens da sua webcam em algum lugar acessível diretamente pela Web
  3. Você não quer colocar várias imagens de câmeras em uma única página ou postar

Tenho certeza de que há muito espaço para melhorias, mas faz o seguinte:

  • Permite que você configure diferentes páginas de cames

A seção 1 é o arquivo principal do plugin. Ele só precisa ser colocado em um arquivo php, dado um cabeçalho de plugin, e colocar na raiz de uma pasta de plugins apropriada.

A seção 2 é o arquivo javascript. O arquivo de plug-in principal considera que ele está em um subdiretório denominado js e possui o nome de arquivo web_cam_checker.js .

Seção 1: O arquivo 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}" );
    }
}

O construtor Parâmetros:

  • image_tag_id: o valor no atributo id da imagem a ser exibido por
  • images_dir: Caminho completo para o diretório no servidor que contém as imagens para esta cam
  • images_url: imagens de URL acessíveis ao mundo podem ser visualizadas em
  • refresh_interval_s: Segundos entre as pulsações desta câmara
  • init_retry_ms: durante o init, quantos milissegundos esperar entre tentativas
  • min_init_retries: o mínimo tenta fazer na inicialização antes de desistir
  • is_debug: se deve ou não mostrar mensagens de depuração no console js
  • do_purge_images: se deve ou não excluir imagens antigas no diretório images_dir

Nota: images_dir e images_url assumem que você criou um cam-images sob o diretório raiz do plugin. Você pode configurá-los para o que você quiser.

Escolher quais páginas precisam do javascript da câmera

Escolhi o padrão para não carregar o javascript e usar um filtro para permitir que o usuário escolha quais páginas precisam do script.

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);

Observe que eu mudei este para assumir que dois objetos cam estão sendo carregados:

  1. Um com id="main_cam_image" na primeira página
  2. Outro com id="second_cam_image" em uma página com o slug 'cam_two'

Ajuste conforme desejado com as várias funções is_ * do wordpress para que o arquivo javscript seja carregado onde desejar.

Além disso, a configuração principal é imediatamente após.

Se você quiser criar um objeto cam em outro lugar (digamos, um arquivo functions.php), você deve ter certeza de usar o namespace (como o seguinte):

$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 );
    }
}

Seção dois: o arquivo Javascript Qualquer dica seria muito apreciada por aqueles mais familiarizados com o javascript. Estou apenas aprendendo, mas fiz o melhor que pude. Isso funciona ... e isso é algo.

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) );
        });

});

Obrigado pela ideia do Kaiser. Eu não tinha ouvido falar da API de heartbeat e eu estava procurando algo para aumentar o conhecimento sobre javascript ... então foi um bom exercício.

Eu só tentei em um servidor LAMP navegando do Firefox ... e não, eu não coloquei uma declaração rigorosa ainda ... mas pode fazer isso da próxima vez que eu tiver algum tempo.

Em qualquer caso, espero que isso ajude alguém um pouco.

Qualquer para quem é novo no código ...

Para fazer isso funcionar:

  1. Crie um diretório em seu diretório wp-content / plugins (nomeie qualquer coisa, eu usarei privateer-web-cam-updates)
  2. Crie um novo arquivo de texto chamado privateer-web-cam-updates.php nesse diretório e abra-o em um editor de texto simples
  3. Adicione uma id="main_cam_image" a uma tag de imagem em seu site (diga sua página inicial). Pode parecer com <img src="#" id="main_cam_image" title="My Web Cam" />
  4. Edite a seção de código a seguir no plug-in.

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);

Se você tiver sua imagem em um blog, altere is_front_page() para is_home() . Se você tiver em uma página, obtenha o ID da página e altere is_front_page() para is_page(n) , onde n é o ID da página.

  1. Crie um subdiretório cam-images na pasta do plugin e configure sua web cam para colocar imagens lá.
  2. Crie um subdiretório js na pasta do plug-in.
  3. Copie o código da Seção 2 para um novo arquivo de texto chamado web_cam_checker.js

E, novamente, a crítica construtiva é sempre apreciada. (parece que a última seção não pode ser detectada como código por algum motivo)

    
por Privateer 19.01.2015 / 06:26