App-EventStreamr/lib/App/EventStreamr/Config.pm
package App::EventStreamr::Config;
use Method::Signatures;
use Sys::Hostname;
use experimental 'say';
use Config::JSON; # libconfig-json-perl
use File::Path qw(make_path);
use Carp 'croak';
use App::EventStreamr::Devices;
use Moo;
use namespace::clean;
# ABSTRACT: A config object
our $VERSION = '0.5'; # VERSION: Generated by DZP::OurPkg:Version
has 'config_path' => ( is => 'rw', default => sub { "$ENV{HOME}/.eventstreamr" } );
has 'config_file' => ( is => 'rw', lazy => 1, builder => 1 );
has 'roles' => ( is => 'rw', default => sub { [ ] } );
has 'backend' => ( is => 'rw', default => sub { "DVswitch" } );
has 'nickname' => ( is => 'rw', lazy => 1, builder => 1 );
has 'room' => ( is => 'rw', lazy => 1, default => sub { 'test_room' } );
has 'record_path' => ( is => 'rw', lazy => 1, default => sub { '/tmp/$room/$date' } );
has 'run' => ( is => 'rw', lazy => 1, default => sub { '0' } );
has 'control' => ( is => 'rw' );
has 'stream' => ( is => 'rw' );
has 'youtube' => ( is => 'rw' );
has 'sync' => ( is => 'rw' );
has 'pid' => ( is => 'ro', lazy => 1, builder => 1);
has 'log_level' => ( is => 'ro', default => sub { 'INFO, LOG1' } );
has 'mixer' => (
is => 'rw',
lazy => 1,
default => sub {
{
host => '127.0.0.1',
port => '1234',
}
}
);
# TODO: Be more intelligent with devices
has 'devices_util' => ( is => 'rw', lazy => 1, builder => 1 );
has 'devices' => ( is => 'rw', default => sub { [ ] } );
has 'available_devices' => ( is => 'rw' );
# TODO: this really needs to be changed to some
# type of GUID
has 'macaddress' => ( is => 'rw', lazy => 1, builder => 1 );
# External utilities
has 'localconfig' => (
is => 'rw',
lazy => 1,
builder => 1,
handles => [ qw( create write config ) ],
);
# DVswitch Checking - Probably better somewhere else
has 'dvswitch_running' => ( is => 'rw', default => sub { '0' } );
sub BUILD {
my $self = shift;
$self->_load_config;
}
method _build_pid() {
return $$;
}
method _build_devices_util() {
return App::EventStreamr::Devices->new();
}
method update_devices() {
$self->{available_devices} = $self->devices_util->all();
}
method _build_config_file() {
return $self->config_path."/config.json";
}
method _build_nickname() {
return hostname || "default_name";
}
method _build_macaddress() {
my $macaddress = `ifdata -ph eth0`;
chomp $macaddress;
return $macaddress;
}
method _create_config_path() {
if ( ! -d $self->config_path ) {
make_path($self->config_path) or
croak "Couldn't create config path $self->{config_path}";
}
}
method _build_localconfig() {
# Config JSON barfs if you try to load a config that doesn't exist,
# this will load one or attempt to create it.
if ( -e $self->config_file ) {
return Config::JSON->new($self->config_file);
} else {
$self->_create_config_path();
my $config = Config::JSON->create($self->config_file);
$config->{config} = {
nickname => $self->nickname,
room => $self->room,
record_path => $self->record_path,
run => $self->run,
backend => $self->backend,
};
$config->write;
return $config;
}
}
method _load_config() {
# TODO: There has to be a better way..
foreach my $key (keys %{$self->localconfig->{config}}) {
$self->{$key} = $self->localconfig->{config}{$key};
}
}
method reload_config() {
$self->_build_localconfig;
}
method _clean_config() {
return {
nickname => $self->nickname,
room => $self->room,
record_path => $self->record_path,
run => $self->run,
backend => $self->backend,
roles => $self->roles,
stream => $self->stream,
youtube => $self->youtube,
sync => $self->sync,
mixer => $self->mixer,
control => $self->control,
devices => $self->devices,
backend => $self->backend,
}
}
method _post_data() {
my $config = $self->_clean_config();
$config->{manager}{pid} = $$;
$config->{macaddress} = $self->macaddress();
return $config;
}
method write_config() {
$self->localconfig->{config} = $self->_clean_config();
$self->localconfig->write;
}
method configure() {
say "Welcome to the EventStreamr config utility\n";
my $answer = $self->prompt(
"It will clear the current config, is this ok? y/n",
"n",
);
exit 1 if ($answer =~ /n/i);
# TODO: Write config clear method
$self->roles([]);
# Config Questions
$self->configure_room();
$self->configure_backend();
$self->configure_mixer();
$self->configure_ingest();
$self->write_config();
say;
# Confirm written.
if (-e $self->config_file) {
say "Config written successfully";
} else {
say "Config failed to write";
}
# TODO: Stream Configuration
#$answer = undef;
#$answer = $self->prompt(
# "Stream - stream to configured service y/n: ",
# "y",
#);
#push(@{$self->{roles}}, "stream") if ($answer =~ /y/i);
}
method configure_mixer() {
my $answer = $self->prompt(
"Mixer - Video mixer interface y/n",
"y",
);
if ($answer =~ /y/i) {
push(@{$self->roles}, "mixer");
$self->configure_remote_mixer();
# We could run gst-switch srv or dvsink
# on separate hosts, but this will do
# for now.
push(@{$self->roles}, "record");
$self->configure_recordpath();
}
}
method configure_ingest() {
my $answer = $self->prompt(
"Ingest - audio/video ingest y/n",
"y",
);
if ($answer =~ /y/i) {
push(@{$self->roles}, "ingest");
$self->update_devices();
$self->devices([]);
foreach my $device (@{$self->{available_devices}{array}}) {
my $ingest = $self->prompt(
"Enable '".$device->{name}."' for ingest",
"y",
);
push(@{$self->devices}, $device) if ($ingest =~ /y/i);
}
$self->configure_remote_mixer() if (! $self->{mixer}{host});
}
}
method configure_remote_mixer() {
# TODO: Setting config like this is terrible,
# figure out how to do it better
my $answer = $self->prompt(
"host - switching host",
"127.0.0.1",
);
$self->{mixer}{host} = $answer ;
$answer = $self->prompt(
"port - switching port",
"1234",
);
$self->{mixer}{port} = $answer;
}
method configure_backend() {
my $answer = $self->prompt(
"backend - DVswitch|GSTswitch",
$self->backend,
);
if ($answer =~ /gst/i) {
$self->backend('GSTswitch');
return;
} elsif ($answer =~ /dv/i) {
$self->backend('DVswitch');
return;
} else {
say "Invalid backend";
$self->configure_backend;
return
}
}
method configure_recordpath() {
say '$room + $date can be used as variables in the path and';
say 'will correctly be set and created at run time';
my $answer = $self->prompt(
"recordpath - ",
'/tmp/$room/$date',
);
$self->record_path($answer);
}
method configure_room() {
my $answer = $self->prompt(
"Room - For record path and controller",
$self->room,
);
$self->room($answer);
}
method prompt($question,$default?) { # inspired from here: http://alvinalexander.com/perl/edu/articles/pl010005
if ($default) {
print $question, " [", $default, "]: ";
} else {
print $question, ": ";
$default = "";
}
$| = 1; # flush
$_ = <STDIN>; # get input
chomp;
if ("$default") {
return $_ ? $_ : $default; # return $_ if it has a value
} else {
return $_;
}
}
with('App::EventStreamr::Roles::Logger','App::EventStreamr::Roles::ConfigAPI');
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::EventStreamr::Config - A config object
=head1 VERSION
version 0.5
=head1 SYNOPSIS
This package provides core configuration methods. Class
will attempt to load the config via a number of different
methods and fall back to a default state.
=head1 DESCRIPTION
Provides access to configuration methods.
=head1 METHODS
=head2 write_config
$config->write_config();
Will write the config out to disk.
=head2 configure
$config->configure();
Will run through configuring EventStreamr.
=head1 AUTHOR
Leon Wright < techman@cpan.org >
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2014 by Leon Wright.
This is free software, licensed under:
The GNU Affero General Public License, Version 3, November 2007
=cut