#!/usr/bin/perl
local $| = 0;
use warnings;
use strict;
use Data::Dumper;
use CGI::Simple ();
use ModPerl::Util ();
use Date::Calc();
use Time::Local();
use File::Temp();
use config();
use log();
use localization();
use auth();
use uac();
use studios();
use series();
use template();
use audio_recordings();
use events();
use time();
#$|=1;
binmode STDOUT, ":utf8";
my $useCgi = 0;
our $config = config::get('../config/config.cgi');
our $debug = $config->{system}->{debug};
my $base_dir = $config->{locations}->{base_dir};
my $tempDir = '/var/tmp';
my $uploadLimit = 300_000_000;
my %params = ();
my $error = '';
my $cgi = undef;
my $fh = undef;
#### simple CGI
$CGI::Simple::POST_MAX = $uploadLimit;
$CGI::Simple::DISABLE_UPLOADS = 0;
$cgi = CGI::Simple->new;
my $filename = $cgi->param('upload');
$fh = $cgi->upload($filename);
$error = $cgi->cgi_error() || '';
%params = $cgi->Vars();
my $params = \%params;
binmode $fh if defined $fh;
#print "Content-type:text/html; charset=UTF-8;\n\n";
my ( $user, $expires ) = auth::get_user( $config, $params, $cgi );
exit if ( !defined $user ) || ( $user eq '' );
my $user_presets = uac::get_user_presets(
$config,
{
user => $user,
project_id => $params->{project_id},
studio_id => $params->{studio_id}
}
);
$params->{default_studio_id} = $user_presets->{studio_id};
$params = uac::setDefaultStudio( $params, $user_presets );
$params = uac::setDefaultProject( $params, $user_presets );
my $request = {
url => $ENV{QUERY_STRING} || '',
params => {
original => $params,
checked => check_params($config, $params),
},
};
$request = uac::prepare_request( $request, $user_presets );
$params = $request->{params}->{checked};
my $headerParams = uac::set_template_permissions( $request->{permissions}, $params );
$headerParams->{loc} = localization::get( $config, { user => $user, file => 'menu' } );
template::process( $config, 'print', template::check($config, 'default.html'), $headerParams );
exit unless defined uac::check( $config, $params, $user_presets );
print q{
} unless (params::isJson);
my $permissions = $request->{permissions};
$params->{action} = '' unless defined $params->{action};
$params->{error} = $error || '';
#print STDERR Dumper($params);
if ( $params->{action} eq 'upload' ) {
uploadRecording( $config, $request );
} elsif ( $params->{action} eq 'delete' ) {
deleteRecording( $config, $request );
}
showAudioRecordings( $config, $request );
print STDERR "$0 ERROR: " . $params->{error} . "\n" if $params->{error} ne '';
$params->{loc} = localization::get( $config, { user => $params->{presets}->{user}, file => 'event,comment' } );
template::process($config, 'print', $params->{template}, $params );
exit;
sub uploadRecording {
my $config = shift;
my $request = shift;
my $params = $request->{params}->{checked};
my $permissions = $request->{permissions};
unless ( $permissions->{upload_audio_recordings} == 1 ) {
uac::permissions_denied('upload_audio_recordings');
return;
}
for my $attr ( 'project_id', 'studio_id', 'series_id', 'event_id' ) {
unless ( defined $params->{$attr} ) {
uac::print_error( "missing " . $attr . " to upload productions" );
return;
}
}
if ( defined $fh ) {
print STDERR "upload\n";
#print STDERR Dumper($fh)."
";
my $fileInfo = uploadFile( $config, $fh, $params->{event_id}, $user, $params->{upload} );
$params->{error} .= $fileInfo->{error} if defined $fileInfo->{error};
$params->{path} = $fileInfo->{path};
$params->{size} = $fileInfo->{size};
#$params->{duration} = $fileInfo->{duration};
$params = updateDatabase( $config, $params, $user ) if $params->{error} eq '';
} else {
$params->{error} .= 'Could not get file handle';
}
if ( $params->{error} ne '' ) {
if ( $params->{error} =~ /limit/ ) {
$params->{error} .=
"audio file size is limited to " . int( $uploadLimit / 1000000 ) . " MB!" . "Please make it smaller and try again!";
} else {
$params->{error} .= "Error:'$error'";
}
}
}
# return 1 if file has been deleted
sub deleteFile {
my $file = shift;
return 0 unless defined $file;
if ( -e $file ) {
if ( -w $file ) {
unlink $file;
# check if file has been deleted
if ( -e $file ) {
uac::print_error("could not delete audio file '$file', $!\n");
return 0;
}
} else {
uac::print_error("cannot delete audio file '$file', missing permissions\n");
return 0;
}
}
return 1;
}
sub deleteRecording {
my $config = shift;
my $request = shift;
my $params = $request->{params}->{checked};
my $permissions = $request->{permissions};
unless ( $permissions->{delete_audio_recordings} == 1 ) {
uac::permissions_denied('delete_audio_recordings');
return;
}
for my $attr (
'project_id', 'studio_id',
#'series_id',
'event_id', 'path'
)
{
unless ( defined $params->{$attr} ) {
uac::print_error( "missing " . $attr . " to delete production" );
return;
}
}
my $dbh = db::connect($config);
$config->{access}->{write} = 0;
my $audioRecordings = audio_recordings::get(
$config,
{
project_id => $params->{project_id},
studio_id => $params->{studio_id},
event_id => $params->{event_id},
path => $params->{path}
}
);
unless ( ( defined $audioRecordings ) && ( scalar @$audioRecordings > 0 ) ) {
uac::print_error("could not find audio file $params->{path} in database");
return;
}
my $targetDir = $config->{locations}->{local_audio_recordings_dir};
unless ( defined $targetDir ) {
uac::print_error("'local_audio_recordings_dir' is not configured.");
return;
}
unless ( -d $targetDir ) {
uac::print_error("audio dir '$targetDir' does not exist");
return;
}
my $file = $targetDir . '/' . $params->{path};
print STDERR "ERROR: cannot delete audio file '$file', file does not exist\n" unless -e $file;
my $isDeleted = deleteFile($file);
return unless $isDeleted;
$config->{access}->{write} = 1;
$audioRecordings = audio_recordings::delete(
$config, $dbh,
{
project_id => $params->{project_id},
studio_id => $params->{studio_id},
event_id => $params->{event_id},
path => $params->{path},
}
);
$config->{access}->{write} = 0;
}
sub showAudioRecordings {
my $config = shift;
my $request = shift;
my $params = $request->{params}->{checked};
my $permissions = $request->{permissions};
for my $attr ( 'project_id', 'studio_id', 'series_id', 'event_id' ) {
unless ( defined $params->{$attr} ) {
uac::print_error( "missing " . $attr . " to show productions" );
return;
}
}
my $event = series::get_event(
$config,
{
project_id => $params->{project_id},
studio_id => $params->{studio_id},
series_id => $params->{series_id},
event_id => $params->{event_id}
}
);
unless ( defined $event ) {
uac::print_error("event not found");
return;
}
#print '
'.Dumper($event).''; my $audioRecordings = audio_recordings::get( $config, { project_id => $params->{project_id}, studio_id => $params->{studio_id}, event_id => $params->{event_id}, } ); #print Dumper($audioRecordings); for my $recording (@$audioRecordings) { $recording->{size} =~ s/(\d)(\d\d\d)$/$1\.$2/g; $recording->{size} =~ s/(\d)(\d\d\d\.\d\d\d)$/$1\.$2/g; $recording->{processed} = $recording->{processed} ? 'yes' : 'no'; $recording->{mastered} = $recording->{mastered} ? 'yes' : 'no'; $recording->{eventDuration} = getDuration( $recording->{eventDuration} ); $recording->{audioDuration} = getDuration( $recording->{audioDuration} ); $recording->{rmsLeft} ||= '-'; $recording->{rmsRight} ||= '-'; } my $now = time(); my $timeZone = $config->{date}->{time_zone}; my $start = time::datetime_to_utc( $event->{start}, $timeZone ); my $end = time::datetime_to_utc( $event->{end}, $timeZone ); if ( $now > $end ) { uac::print_error("upload is expired due to the show is over"); $params->{isOver} = 1; } my $days = 24 * 60 * 60; uac::print_warn("show is more than a week ahead") if ( $now + 7 * $days ) < $start; $params->{event} = $event; $params->{audio_recordings} = $audioRecordings; } sub getDuration { my $duration = shift; my $hour = int( $duration / 3600 ); $duration -= $hour * 3600; my $minutes = int( $duration / 60 ); $duration -= $minutes * 60; my $seconds = int($duration); $duration -= $seconds; my $milli = int( 100 * $duration ); return sprintf( "%02d:%02d:%02d.%02d", $hour, $minutes, $seconds, $milli ); } sub uploadFile { my $config = $_[0]; my $fh = $_[1]; my $eventId = $_[2]; my $user = $_[3] || ''; my $filename = $_[4] || ''; # check target directory my $targetDir = $config->{locations}->{local_audio_recordings_dir}; return { error => "could not find local_audio_recordings_dir" } unless defined $targetDir; return { error => "local_audio_recordings_dir does not exist" } unless -e $targetDir; # save file to disk my $userName = $user; $userName =~ s/[^a-zA-Z0-9\.\-\_]//g; my $time = time::time_to_datetime(); $time =~ s/\:/\-/g; $time =~ s/\s/\_/g; $time =~ s/[^a-zA-Z0-9\.\-\_]//g; $filename =~ s/\.(mp3)$//g; $filename = join( '-', ( $time, 'id' . $eventId, $userName, $filename ) ) . '.mp3'; $filename =~ s/[^a-zA-Z0-9\.\-\_]//g; my $tempFile = $targetDir . '/' . $filename; print STDERR "tempFile=$tempFile\n"; my $start = time(); open DAT, '>', $tempFile or return { error => 'could not save upload. ' . $! . " " . $tempFile }; binmode DAT; my $size = 0; my $data = ''; while ( my $bytesRead = $fh->read( $data, 65000 ) ) { print DAT $data; $size += $bytesRead; $data = ''; } close DAT; return { dir => $targetDir, path => $filename, size => $size, }; } sub updateDatabase { my $config = shift; my $params = shift; my $user = shift; my $eventDuration = getEventDuration( $config, $params->{event_id} ); my $entry = { project_id => $params->{project_id}, studio_id => $params->{studio_id}, event_id => $params->{event_id}, path => $params->{path}, size => $params->{size}, created_by => $user, eventDuration => $eventDuration }; #print STDERR "updateDatabase:" . Dumper($entry); #connect $config->{access}->{write} = 1; my $dbh = db::connect($config); my $entries = audio_recordings::get( $config, { project_id => $entry->{project_id}, studio_id => $entry->{studio_id}, event_id => $entry->{event_id}, path => $entry->{path} } ); if ( ( defined $entries ) && ( scalar @$entries > 0 ) ) { print STDERR "update\n"; audio_recordings::update( $config, $dbh, $entry ); my $entry = $entries->[0]; $params->{id} = $entry->{id}; } else { print STDERR "insert\n"; $entry->{created_by} = $user; $entry->{processed} = 0; $entry->{mastered} = 0; $entry->{rmsLeft} = 0.0; $entry->{rmsRight} = 0.0; $entry->{audioDuration} = 0.0; $entry->{modified_at} = time(); $params->{id} = audio_recordings::insert( $config, $dbh, $entry ); } $config->{access}->{write} = 0; $params->{action_result} = 'done!'; return $params; } # return filename, filehandle and optionally error from upload sub getFilename { my $cgi = shift; my $upload = shift; if ( defined $upload ) { # try apache2 module my $filename = $upload->filename(); return { filename => $filename, fh => $upload->fh(), error => '' }; } #print STDERR "cgi:".Dumper($cgi); # fallback to CGI module my $file = $cgi->param("upload"); return { error => "is no file" } if ( defined $file ) && ( $file =~ /\|/ ); #print STDERR "file:".Dumper($file); my $fileInfo = $cgi->uploadInfo($file); #print STDERR "fileInfo:".Dumper($fileInfo); if ( defined $fileInfo ) { my $filename = $fileInfo->{'Content-Disposition'} || ''; if ( $filename =~ /filename=\"(.*?)\"/ ) { $filename = $1; return { filename => $filename, fh => $file, error => '' }; } } #error return { error => 'Could not detect file name!' }; } # get extension and optionally error sub checkFilename { my $filename = shift; my @validExtensions = ('mp3'); if ( $filename =~ /\.([a-zA-Z]{3,5})$/ ) { my $extension = lc $1; unless ( grep( /$extension/, @validExtensions ) ) { return { error => 'Following file formats are supported: ' . join( ",", @validExtensions ) . '!' }; } return { extension => $extension, error => '' }; } return { error => 'Not matching file extension found! Supported are: ' . join( ",", @validExtensions ) . '!' }; } # return event duration in seconds sub getEventDuration { my $config = shift; my $eventId = shift; if ( $eventId < 1 ) { print STDERR "invalid eventId $eventId\n"; return 0; } my $request = { params => { checked => events::check_params( $config, { event_id => $eventId, template => 'no', limit => 1, } ) }, config => $config }; $request->{params}->{checked}->{published} = 'all'; my $events = events::get( $config, $request ); if ( scalar @$events == 0 ) { print STDERR "getEventDuration: no event found with event_id=$eventId\n"; } my $event = $events->[0]; my $duration = time::get_duration_seconds( $event->{start}, $event->{end}, $config->{date}->{time_zone} ); return $duration; } sub check_params { my $config = shift; my $params = shift; my $checked = {}; $checked->{error} = ''; $checked->{template} = template::check($config, $params->{template}, 'upload_audio_recordings' ); #print Dumper($params); #numeric values for my $param ( 'project_id', 'studio_id', 'default_studio_id', 'series_id', 'event_id', 'id' ) { if ( ( defined $params->{$param} ) && ( $params->{$param} =~ /^\d+$/ ) ) { $checked->{$param} = $params->{$param}; } } if ( defined $checked->{studio_id} ) { $checked->{default_studio_id} = $checked->{studio_id}; } else { $checked->{studio_id} = -1; } #word for my $param ( 'debug', 'name', 'description' ) { if ( ( defined $params->{$param} ) && ( $params->{$param} =~ /^\s*(.+?)\s*$/ ) ) { $checked->{$param} = $1; } } # words for my $attr ( 'action', 'path' ) { if ( ( defined $params->{$attr} ) && ( $params->{$attr} =~ /(\S+)/ ) ) { $checked->{$attr} = $params->{$attr}; } } $checked->{upload} = $params->{upload}; return $checked; }