File Coverage

blib/lib/Mojolicious/Plugin/PNGCast.pm
Criterion Covered Total %
statement 20 74 27.0
branch 0 4 0.0
condition 0 3 0.0
subroutine 7 13 53.8
pod 2 2 100.0
total 29 96 30.2


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::PNGCast;
2 1     1   674 use strict;
  1         3  
  1         26  
3 1     1   18 use 5.014;
  1         4  
4 1     1   524 use Mojo::Base 'Mojolicious::Plugin';
  1         124077  
  1         7  
5 1     1   1562 use Filter::signatures;
  1         16598  
  1         7  
6 1     1   36 use feature 'signatures';
  1         3  
  1         36  
7 1     1   6 no warnings 'experimental::signatures';
  1         2  
  1         32  
8              
9 1     1   468 use Future::Mojo;
  1         146867  
  1         839  
10              
11             our $VERSION = '0.22';
12              
13             =head1 NAME
14              
15             Mojolicious::Plugin::PNGCast - in-process server to display a screencast
16              
17             =head1 DESCRIPTION
18              
19             Use this web application to display the screencast of a (headless) web browser
20             or other arbitrary PNG data sent to it via websocket.
21              
22             The synopsis shows how to use this plugin to display
23             a Chrome screencast using L.
24              
25             =head1 SYNOPSIS
26              
27             use Mojolicious::Lite;
28             use Mojo::Server::Daemon;
29             use WWW::Mechanize::Chrome;
30             plugin 'PNGCast';
31              
32             my $daemon_url = 'http://localhost:3000';
33              
34             my $ws_monitor = Mojo::Server::Daemon->new(app => app());
35             $ws_monitor->listen([$daemon_url]);
36             $ws_monitor->start;
37              
38             my $mech = WWW::Mechanize::Chrome->new( headless => 1 );
39             $mech->setScreenFrameCallback( sub {
40             app->send_frame( $_[1]->{data} )}
41             );
42              
43             print "Watch progress at $daemon_url\n";
44             sleep 5;
45              
46             $mech->get('https://example.com');
47              
48             =cut
49              
50             has 'clients' => sub { {} };
51             has 'last_frame' => undef;
52              
53             =head1 HELPERS
54              
55             =head2 C<< app->send_frame >>
56              
57             app->send_frame( $png_data );
58              
59             Sends a frame to all connected clients. If a fresh client connects, it will
60             receive the last frame handed to C.
61              
62             =head1 INTERNAL METHODS
63              
64             =head2 C<< $plugin->notify_clients >>
65              
66             $plugin->notify_clients( $PNGframe )
67              
68             Notify all connected clients that they should display the new frame.
69              
70             =cut
71              
72 0     0 1   sub notify_clients( $self, @frames ) {
  0            
  0            
  0            
73 0           my $clients = $self->clients;
74 0           for my $client_id (sort keys %$clients ) {
75 0           my $client = $clients->{ $client_id };
76 0           for my $frame (@frames) {
77 0           eval {
78 0           $client->send({ binary => $frame });
79             };
80             };
81             };
82             }
83              
84 0     0 1   sub register( $self, $app, $config ) {
  0            
  0            
  0            
  0            
85              
86             $app->routes->get('/' => sub {
87 0     0     my( $c ) = @_;
88 0           $c->res->headers->content_type('text/html');
89 0           $c->res->headers->connection('close');
90 0           $c->render('index')
91 0           });
92              
93             $app->routes->websocket( '/ws' => sub {
94 0     0     my( $c ) = @_;
95 0           $c->inactivity_timeout(300);
96              
97 0   0       my $client_id = join ":", $c->tx->original_remote_address || $c->tx->remote_address,
98             $c->tx->remote_port();
99              
100 0           $self->clients->{ $client_id } = $c;
101             $c->tx->on( json => sub {
102 0           my( $c, $data ) = @_;
103             #warn Dumper $data ;
104 0           warn "Click received (and ignored)";
105             #$mech->click( { selector => '//body', single => 1 }, $data->{x}, $data->{y} );
106             #$mech->click( { selector => '//body', single => 1 }, $data->{x}, $data->{y} );
107              
108 0           });
109             #warn("Client connected");
110 0 0         if( $self->last_frame ) {
111             # send current frame
112 0           $c->send({ binary => $self->last_frame });
113             } else {
114             # send a standby frame ??
115             };
116             $c->tx->on( finish => sub {
117 0           my( $c,$code,$reason ) = @_;
118 0           warn "Client gone ($code,$reason)" ;
119 0           delete $self->clients->{ $client_id };
120 0           });
121 0           });
122              
123             # Stop our program
124             $app->routes->get( '/stop' => sub {
125 0     0     my( $c ) = @_;
126 0           $c->res->headers->content_type('text/html');
127 0           $c->res->headers->connection('close');
128 0           $c->render('stop');
129 0           Mojo::IOLoop->stop;
130 0           });
131              
132 0     0     $app->helper( 'send_frame' => sub ( $c, $framePNG ) {
  0            
  0            
  0            
133             # send this frame to all connected clients
134 0 0         if( scalar keys %{ $self->clients } ) {
  0            
135             Future::Mojo->new->done_next_tick( 1 )
136             ->then( sub {
137 0           $self->notify_clients( $framePNG );
138 0           })->retain;
139             };
140 0           $self->last_frame( $framePNG );
141 0           });
142              
143             # Install our templates
144 0           push @{$app->renderer->classes}, __PACKAGE__;
  0            
145 0           push @{$app->static->classes}, __PACKAGE__;
  0            
146             }
147              
148             =head1 EXPORTED HTTP ENDPOINTS
149              
150             This plugin makes the following endpoints available
151              
152             =over 4
153              
154             =item *
155              
156             C - the index page
157              
158             This is an HTML page that opens a websocket to the webserver and listens for
159             PNG images coming in over that websocket
160              
161             =item *
162              
163             C - the websocket
164              
165             This is a websocket
166              
167             =item *
168              
169             C - stop the application
170              
171             This stops the complete Mojolicious application
172              
173             =back
174              
175             =head1 REPOSITORY
176              
177             The public repository of this module is
178             L.
179              
180             =head1 SUPPORT
181              
182             The public support forum of this module is L.
183              
184             =head1 BUG TRACKER
185              
186             Please report bugs in this module via the RT CPAN bug queue at
187             L
188             or via mail to L.
189              
190             =head1 AUTHOR
191              
192             Max Maischein C
193              
194             =head1 COPYRIGHT (c)
195              
196             Copyright 2010-2018 by Max Maischein C.
197              
198             =head1 LICENSE
199              
200             This module is released under the same terms as Perl itself.
201              
202             =cut
203              
204             1
205             __DATA__