App-SlideServer

 view release on metacpan or  search on metacpan

lib/App/SlideServer.pm  view on Meta::CPAN

	$self->published_state->%* = ( $self->published_state->%*, @new_attrs );
	$_->send({ json => { state => $self->published_state } })
		for values $self->viewers->%*;
}


sub startup($self) {
	$self->build_slides;
	$self->presenter_key;
	$self->static->paths([ $self->serve_dir->child('public'), $self->share_dir->child('public') ]);
	$self->routes->get('/' => sub($c){ $c->app->serve_page($c) });
	$self->routes->websocket('/slidelink.io' => sub($c){ $c->app->init_slidelink($c) });
}

sub serve_page($self, $c, %opts) {
	if (!defined $self->page_dom || $self->cache_token) {
		eval { $self->build_slides; 1 }
			or $self->log->error($@);
	}
	# Merge the empty page with all currently-visible slides,
	# which saves the client from needing a second request to fetch them.
	# TODO: implement slide-by-slide loading
	my $slide_max= $#{$self->slides_dom}; # $self->published_state->{slide_max} || 0;
	my @slides= $self->slides_dom->@[0..$slide_max];
	my $combined= Mojo::DOM->new($self->page_dom);
	$combined->at('div.slides')->append_content(join '', @slides);

	# If this is for the presenter, set the config variable for that
	if ($opts{presenter} || defined $c->req->param('presenter')) {
		$combined->at('head')->append_content(
			'<script>window.slides.config.mode="presenter";</script>'."\n"
		);
	}

	$c->render(text => ''.$combined);
}

sub init_slidelink($self, $c) {
	my $id= $c->req->request_id;
	$self->viewers->{$id}= $c;
	my $mode= $c->req->param('mode');
	my $key= $c->req->param('key');
	my %roles= ( follow => 1 );
	if ($mode eq 'presenter') {
		if (($key||'') eq $self->presenter_key) {
			$roles{lead}= 1;
			$roles{navigate}= 1;
			$self->update_published_state(viewer_count => scalar keys $self->viewers->%*);
		}
		elsif (defined $key) {
			$c->send({ json => { key_incorrect => 1 } });
		}
	}
	$c->stash('roles', join ',', keys %roles);
	$self->log->info(sprintf "%s (%s) connected as %s", $id, $c->tx->remote_address, $c->stash('roles'));
	$c->send({ json => { roles => [ keys %roles ] } });
	
	$c->on(json => sub($c, $msg, @) { $c->app->on_viewer_message($c, $msg) });
	$c->on(finish => sub($c, @) { $c->app->on_viewer_disconnect($c) });
	$c->inactivity_timeout(3600);
	#my $keepalive= Mojo::IOLoop->recurring(60 => sub { $viewers{$id}->send([1, 0, 0, 0, WS_PING, '']); });
	#$c->stash(keepalive => $keepalive);
}


sub on_viewer_message($self, $c, $msg) {
	my $id= $c->req->request_id;
	$self->log->debug(sprintf "client %s %s msg=%s", $id, $c->tx->original_remote_address//'', $msg//'');
	if ($c->stash('roles') =~ /\blead\b/) {
		if (defined $msg->{extern}) {
		}
		if (defined $msg->{slide_num}) {
			$self->update_published_state(
				slide_num => $msg->{slide_num},
				step_num => $msg->{step_num},
				($msg->{slide_num} > ($self->published_state->{slide_max}//0)?
					( slide_max => $msg->{slide_num} ) : ()
				)
			);
		}
	}
#	if ($c->stash('roles') =~ /\b
}

sub on_viewer_disconnect($self, $c) {
	my $id= $c->req->request_id;
	#Mojo::IOLoop->remove($keepalive);
	delete $self->viewers->{$id};
	$self->update_published_state(viewer_count => scalar keys $self->viewers->%*);
}

sub on_page_changed($self) {
	$_->send({ json => { page_changed => 1 } })
		for values $self->viewers->%*;
}

sub on_slides_changed($self, $changed) {
	my @changes= map +{ idx => $_, html => $self->slides_dom->[$_] }, @$changed;
	for my $viewer (values $self->viewers->%*) {
		$viewer->send({ json => { slides_changed => \@changes } })
	}
}


use Exporter 'import';
our @EXPORT_OK= qw( mojo2logany );

# Utility method to create a Mojo logger that logs to Log::Any
sub mojo2logany($logger= undef) {
	require Mojo::Log;
	require Log::Any;
	if (defined $logger && !ref $logger) {
		$logger= Log::Any->get_logger(category => $logger);
	} elsif (!defined $logger) {
		$logger= Log::Any->get_logger;
	}
	my $mlog= Mojo::Log->new;
	$mlog->unsubscribe('message');
	$mlog->on(message => sub($app, $level, @lines) { $logger->$level(join ' ', @lines) });
	$mlog;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::SlideServer - Mojo web server that serves slides and websocket

=head1 VERSION

version 0.002

=head1 SYNOPSIS

   use App::SlideServer;
   my $app= App::SlideServer->new(\%opts);
   $app->start(qw( daemon -l http://*:2000 ));

=head1 DESCRIPTION

This class is a fairly simple Mojo web application that serves a small



( run in 1.176 second using v1.01-cache-2.11-cpan-df04353d9ac )