You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ngcp-panel/lib/NGCP/Panel/Utils/Sounds.pm

375 lines
12 KiB

package NGCP::Panel::Utils::Sounds;
use strict;
use warnings;
use English;
use Capture::Tiny qw(capture);
use File::Temp qw/tempfile/;
use NGCP::Panel::Utils::Sems;
use NGCP::Panel::Utils::Preferences;
use File::Slurp;
use File::Basename;
sub transcode_file {
my ($tmpfile, $source_codec, $target_codec) = @_;
my $rc = 0;
my ($cmd, $out, $err);
my $is_gsm = 0;
my @conv_args;
# check if the wav is gsm 6.10 encoded
if (lc($source_codec) eq 'wav') {
$cmd = join(' ', '/usr/bin/file', $tmpfile);
($out, $err, $rc) = capture {
system($cmd);
};
if ($out =~ /GSM 6\.10/) {
$is_gsm = 1;
@conv_args = (qw/--encoding gsm --type sndfile/);
}
}
SWITCH: for ($target_codec) {
/^PCMA$/ && do {
@conv_args = (@conv_args, $tmpfile, qw/--type raw --bits 16 --channels 1 -e a-law --rate 16k -/);
last SWITCH;
};
/^WAV$/ && do {
if ($source_codec eq 'PCMA') {
# this can actually only come from inside
# certain files will be stored as PCMA (for handles with name "music_on_hold")
@conv_args = (qw/-A --rate 16k --channels 1 --type raw/, $tmpfile, qw/--type wav -/);
}
else {
@conv_args = (@conv_args, $tmpfile, qw/--type wav --bits 16 --rate 16k -/);
}
last SWITCH;
};
/^MP3$/ && do {
@conv_args = (@conv_args, $tmpfile, qw/--type mp3 --rate 16k -/);
last SWITCH;
};
/^OGG$/ && do {
@conv_args = (@conv_args, $tmpfile, qw/--type ogg --rate 16k -/);
last SWITCH;
};
# default
} # SWITCH
$cmd = join(' ', '/usr/bin/sox', '-V1', @conv_args);
($out, $err, $rc) = capture {
system($cmd);
};
if ($rc != 0 && $err) {
die "Cannot transcode sound file is_gsm=$is_gsm source_codec=$source_codec target_codec=$target_codec cmd=($cmd) error: $err";
}
return $out;
}
sub transcode_data {
my ($data, $source_codec, $target_codec) = @_;
my ($fh, $filename) = tempfile;
print $fh (ref $data ? $$data : $data);
close $fh;
my $out = transcode_file($filename, $source_codec, $target_codec);
unlink $filename;
return \$out;
}
sub stash_soundset_list {
my (%params) = @_;
my $c = $params{c};
my $contract = $params{contract};
my $fetch_parents = $params{fetch_parents} // 0;
my $sets_rs = $c->model('DB')->resultset('voip_sound_sets')->search({
},{
join => 'parent',
});
my $dt_fields = [
{ name => 'id', search => 1, title => $c->loc('#') },
{ name => 'name', search => 1, title => $c->loc('Name') },
{ name => 'description', search => 1, title => $c->loc('Description') },
{ name => 'parent.name', search => 1, title => $c->loc('Parent') },
];
if ($c->user->roles eq "admin") {
splice @{ $dt_fields }, 1, 0,
{ name => 'reseller.name', search => 1, title => $c->loc('Reseller') };
splice @{ $dt_fields }, 2, 0,
{ name => 'contract.contact.email', search => 1, title => $c->loc('Customer') };
push @{ $dt_fields },
{ name => 'expose_to_customer', search => 1, title => $c->loc('Expose to Customer') };
} elsif ($c->user->roles eq "reseller") {
splice @{ $dt_fields }, 1, 0,
{ name => 'contract.contact.email', search => 1, title => $c->loc('Customer') };
push @{ $dt_fields },
{ name => 'expose_to_customer', search => 1, title => $c->loc('Expose to Customer') };
$sets_rs = $sets_rs->search({ 'me.reseller_id' => $c->user->reseller_id });
}
if ($contract || $c->user->roles eq "subscriberadmin") {
my $contract = $contract;
unless ($contract) {
my $contract_rs = $c->stash->{contract_rs} //
NGCP::Panel::Utils::Contract::get_contract_rs(schema => $c->model('DB'))->search_rs({
'me.id' => $c->user->account_id,
});
$contract = $contract_rs->first;
}
my $user_role = $c->user->roles;
my $user_contract_id = $contract->id;
$sets_rs = $sets_rs->search({
-or => [
'me.contract_id' => $contract->id,
-and => [ 'me.contract_id' => undef,
'me.reseller_id' => $contract->contact->reseller_id,
'me.expose_to_customer' => 1,
],
],
});
if (!$fetch_parents) {
$sets_rs = $sets_rs->search({
},{
'+select' => [ { '' => \[ "select '$user_role'" ], -as => 'user_role' },
{ '' => \[ "select '$user_contract_id'" ], -as => 'user_contract_id' },
'me.contract_id' ,
]
});
}
push @{ $dt_fields },
{ name => 'user_role', visible => 0, search => 0, title => $c->loc('#UserRole') },
{ name => 'user_contract_id', visible => 0, search => 0, title => $c->loc('#UserContractId') },
{ name => 'contract_id', visible => 0, search => 0, title => $c->loc('#Contract_id') };
}
$c->stash->{soundset_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, $dt_fields);
$c->stash(sets_rs => $sets_rs);
return;
}
sub get_file_handles {
my (%params) = @_;
my $c = $params{c};
my $set_id = $params{set_id};
my @file_handles = ();
my $handles_rs = $c->model('DB')->resultset('voip_sound_handles')->search({
},{
alias => 'handles',
select => [
'group.name','handles.name', 'handles.id',
],
as => [
'group_name', 'handle_name', 'handle_id',
],
join => 'group',
order_by => { -asc => 'handles.name' }
});
unless($c->config->{features}->{cloudpbx}) {
$handles_rs = $handles_rs->search({ 'group.name' => { '!=' => 'pbx' } });
}
unless($c->config->{features}->{cloudpbx} || $c->config->{features}->{musiconhold}) {
$handles_rs = $handles_rs->search({ 'group.name' => { '!=' => 'music_on_hold' } });
}
unless($c->config->{features}->{callingcard}) {
$handles_rs = $handles_rs->search({ 'group.name' => { '!=' => 'calling_card' } });
}
unless($c->config->{features}->{mobilepush}) {
$handles_rs = $handles_rs->search({ 'group.name' => { '!=' => 'mobile_push' } });
}
my $files_rs = $c->model('DB')->resultset('voip_sound_files')->search({
'me.set_id' => $set_id,
});
my %files = ();
foreach my $file ($files_rs->all) {
$files{$file->handle_id} = {
filename => $file->filename,
loopplay => $file->loopplay,
codec => $file->codec,
file_id => $file->id,
use_parent => $file->use_parent // 1,
}
}
foreach my $handle ($handles_rs->all) {
my %file_handle = $handle->get_inflated_columns;
my $file = $files{$file_handle{handle_id}} // {
filename => undef,
loopplay => undef,
codec => undef,
file_id => undef,
use_parent => undef,
};
push @file_handles, {%file_handle, %{$file}};
}
return \@file_handles;
}
sub apply_default_soundset_files{
my (%params) = @_;
my ($c, $lang, $set_id, $file_handles, $loopplay, $override, $error_ref) = @params{qw/c lang set_id file_handles loopplay override error_ref/};
$loopplay = $loopplay ? 1 : 0;
my $schema = $c->model('DB');
my $base = "/var/lib/ngcp-soundsets";
foreach my $h (@{$file_handles}) {
my $handle_name = $h->{handle_name};
my @paths = (
"$base/system/$lang/$handle_name.wav",
"$base/customer/$lang/$handle_name.wav",
"/var/lib/asterisk/sounds/$lang/digits/$handle_name.wav",
);
my $path;
foreach my $p(@paths) {
if(-f $p) {
$path = $p;
last;
}
}
next unless(defined $path);
my $data_ref;
my $codec = 'WAV';
my $handle_id = $h->{handle_id};
my $file_id = $h->{file_id};
my $fres;
my $fname = basename($path);
read_file($path, buf_ref => \$data_ref);
unless (${data_ref}) {
$$error_ref = "Cannot upload an empty sound file, $fname";
die $$error_ref;
}
if (defined $file_id) {
if ($override) {
$c->log->debug("override $path as $handle_name for existing id $file_id");
$fres = $schema->resultset('voip_sound_files')->find($file_id);
$fres->update({
filename => $fname,
data => ${data_ref},
loopplay => $loopplay,
});
} else {
$c->log->debug("skip $path as $handle_name exists via id $file_id and override is not set");
}
} else {
$c->log->debug("inserting $path as $handle_name with new id");
$fres = $schema->resultset('voip_sound_files')
->create({
filename => $fname,
data => ${data_ref},
handle_id => $handle_id,
set_id => $set_id,
loopplay => $loopplay,
codec => $codec,
});
}
next unless defined($fres);
my $group_name = $fres->handle->group->name;
NGCP::Panel::Utils::Sems::clear_audio_cache($c, $fres->set_id,
$fres->handle->name, $group_name);
}
}
sub contract_sound_set_propagate {
my ($c, $contract, $value) = @_;
for my $bill_subscriber ($contract->voip_subscribers->all) {
my $prov_subscriber = $bill_subscriber->provisioning_voip_subscriber;
if ($prov_subscriber) {
&subcriber_sound_set_update_or_create($c, $prov_subscriber, $value);
}
}
}
sub subcriber_sound_set_update_or_create {
my ($c, $prov_subscriber, $value) = @_;
my $pref_rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(c => $c,
prov_subscriber => $prov_subscriber, attribute => 'contract_sound_set',
);
my $row = $pref_rs->first;
if (!$row) {
$pref_rs->create({ value => $value });
} else {
# Update only undefined sound set value.
$row->update({ value => $value }) if ! defined $row->value;
}
}
sub check_parent_chain_for_loop {
my ($c, $set_id, $parent_id) = @_;
return 0 if !$set_id;
return 0 if !$parent_id;
return 1 if $set_id == $parent_id;
my $rs = $c->model('DB')->resultset('v_sound_set_files')->search({
'me.set_id' => $parent_id,
});
if ($rs->first) {
if (my $parent_chain = $rs->first->parent_chain) {
my @parents = split /:/, $parent_chain;
foreach my $chain_parent_id (@parents) {
if ($set_id == $chain_parent_id) {
return 1;
}
}
}
}
return 0;
}
sub revoke_exposed_sound_set {
my ($c, $set_id) = @_;
my $used_customer_sets_rs = $c->model('DB')->resultset('voip_sound_sets')->search({
parent_id => $set_id,
contract_id => { '!=' => undef },
});
$used_customer_sets_rs->update({ parent_id => undef });
my $used_subscriber_prefs_rs = $c->model('DB')->resultset('voip_usr_preferences')->search({
'attribute.attribute' => 'sound_set',
value => $set_id,
},{
join => 'attribute',
});
$used_subscriber_prefs_rs->delete;
}
1;
# vim: set tabstop=4 expandtab: