Max attachment size, or filtering on file size?

Forum for questions and support relating to the 1.34.x releases only.
Post Reply
goldielocks
Posts: 11
Joined: Wed Oct 21, 2020 9:22 am

Max attachment size, or filtering on file size?

Post by goldielocks »

Hey all,

I have the event video in my email alerts. 95% of the time it's perfect and works fine, but just occasionally (when there is a long alert) it correctly attempts to attach the video, but fails to send it as its way too big to go over email (300MB+ in one example). Looking at the logs it's also taking a huge amount of time uploading this before it then fails to send the email.

Is there a way of me filtering out these events based on file size? I can't see a file size option on the filters page. Or can I do something to stop it trying to attach a file that's over a certain size and just skip the video attachment part for these events? It's really delaying subsequent emails going out as it takes so long on a wasted effort. Any suggestions welcome. Some kind of "max attachment size" in zoneminder would be nice, if it doesn't already exist?

Thanks in advance!
goldielocks
Posts: 11
Joined: Wed Oct 21, 2020 9:22 am

Re: Max attachment size, or filtering on file size?

Post by goldielocks »

Couldn't find anything about this that exists already, so wrote a custom script to do this. Not sure if it'll help anyone and it's my first ever attempt at PERL (I'm a C# desktop developer by day) but figured I'd add here just incase.

Before you get going, make sure you have email setup, and I also have ssmtp as I have an external SMTP server I use on my instance.

So, create a new file:

Code: Select all

/usr/bin/zm-emailer 
Then copy the contents in below:

Code: Select all

#!/usr/bin/perl -w

use strict;
use warnings;
use ZoneMinder;
use MIME::Lite;

# perl thing, flush after every write
$| = 1;

# declare our variables, i.e. url to zoneminder, and from/to email addresses
my $baseUrl = 'https://example.com/zm/';
my $sender = 'sender@test.com;
my $recipient = 'recipient@test.com;

# declare the max size for the email attachements in total, this is in MB
my $maxEmailSize = 24.8;

# get event id from first argument, passed with token %EI% in zoneminder
my $eventId = $ARGV[0];
# zoneminder automatically passes folder path as next argument
my $folderPath = $ARGV[1];

# the folder always has a snapshot.jpg, alarm.jpg, and mp4 video in it for the event.
# build the paths to the files
my $snapshotPath = $folderPath."/snapshot.jpg";
my $alarmPath = $folderPath."/alarm.jpg";
my $videoPath = $folderPath."/".$eventId."-video.mp4";

# get the size of the video path as I want that included in the email
my $videoSize = -s $videoPath;
my $videoSizeMb = $videoSize / (1024 * 1024);
# generate a variable with the video size to 2 decimal places, for the email body
my $videoSizeMbTrimmed = sprintf("%.2f", $videoSizeMb);

# get the size of both jpg files combined for our calculations later
my $imageSizeMb = (-s $snapshotPath) / (1024 * 1024);
$imageSizeMb += (-s $alarmPath) / (1024 * 1024);

# connect to the zoneminder sql database to query the events table for the extra fields needed
# currently only monitorid for the url, endtime and length for the email
my @events;
my $dbh = zmDbConnect();
my $eventsSql = "SELECT MonitorId, EndTime, Length FROM Events Where ID = ".$eventId;
my $sth = $dbh->prepare_cached( $eventsSql ) or die( "Can't prepare '$eventsSql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute '$eventsSql': ".$sth->errstr() );

# load the sql results into the events array
while ( my $events = $sth->fetchrow_hashref() ) {
    push( @events, $events );
}

# extract the fields we queried into fields
my $eventMonitorId = ($events[-1]->{MonitorId});
my $eventDtTm = ($events[-1]->{EndTime});
my $eventLength = ($events[-1]->{Length});

# query the Monitors table for the monior name for our monitor id, for the email body
my $monitorName = $dbh->selectrow_array("SELECT Name from Monitors Where ID = ".$eventMonitorId);

# build up the event URL for the email
my $url = $baseUrl."index.php?view=event&mode=stream&mid=$eventMonitorId&eid=$eventId";

# build up the subject of the email
my $subject   = "ZoneMinder: Alarm - ".$monitorName;

# build up the body of the email, note the video size line is trimmed to 2 decimal places
my $data = qq{ An alarm has been detected on your installation of the ZoneMinder.

The details are as follows:

Event ID:       $eventId
Monitor:        $monitorName
Time:           $eventDtTm
Length:         $eventLength seconds
Video size:     $videoSizeMbTrimmed MB

The alarm was matched and sent out by the custom script and the event can be viewed at $url
};

# create the MIME email object with everything from above
my $mime = MIME::Lite->new(
    'From'    => $sender,
    'To'      => $recipient,
    'Subject' => $subject,
    'Type'    => 'text/plain',
    'Data'    => $data
);

# attach the snapshot jpg
$mime->attach (
   Type => 'image/jpeg',
   Path => $snapshotPath,
   Filename => 'Snapshot-'.$eventId.'.jpg',
   Disposition => 'attachment'
) or die "Error adding $snapshotPath: $!\n";

# attach the alarm jpg
$mime->attach (
   Type => 'image/jpeg',
   Path => $alarmPath,
   Filename => 'Alarm-'.$eventId.'.jpg',
   Disposition => 'attachment'
) or die "Error adding $alarmPath: $!\n";

# attach the video file, if under the specified size
$mime->attach (
   Type => 'video/mpeg',
   Path => $videoPath,
   Filename => 'Video-'.$eventId.'.mp4',
   Disposition => 'attachment'
) or die "Error adding $videoPath: $!\n" if $videoSizeMb + $imageSizeMb < $maxEmailSize;

# send the email
$mime->send() or die "Failed to send mail\n";
You need to set some variables at the top - base url to ZoneMinder, sender email address, recipient email address, and max email size (set here to 24MB as gmail only allows 25MB and remember a few bytes will be used by the body of the email).

Then once created, test that it works by calling it as such:

Code: Select all

/usr/bin/zm-emailer 8604 /mnt/sdb1/zoneminder/events/7/2020-11-03/8604
Substituting in a real event ID and path from your instance.

And assuming that it works fine, create a filter in the ZoneMinder interface, ticking the box to 'execute command on all matches' and use the following as the command:

Code: Select all

/usr/bin/zm-emailer %EI%
and make sure to tick the Background execution too. Then all will work!

If this matches loads of events if your system has been going a while, you may want to set the Executed field in the SQL directly so that it thinks it's already done the existing events, otherwise when you save the filter it's first job will be to email out every single existing match in the database. Connecting to the database and executing the below did the job for me:

Code: Select all

Update Events set executed = 1
And obviously you can edit the email body/subject etc to your liking too.

Hope this helps somebody like it did me!
Post Reply