Thursday, February 23, 2012

Mount/Unmount USB pendrive/USB Harddisk

We have seen sometimes automounting of external drives(USB Pendrive/USB harddisk) work and sometimes it simply does not work. The pain increase when there are multiple partitions in the external drive. To resolve this issue, I have written a perl script to mount/unmount multiple partitions.

The following perl script is useful in following cases:
1. Mounting/Unmounting(-u flag for unmounting) partitions in external drives.
2. Mounting/Unmounting partitions of internal drives too (-d , to (mount to)/(unmount from) any specific location).
3. This script also recognizes single vfat(FAT32) partition(When filesystem was created across the entire device: please refer mkfs.vfat -I) pen-drive too.
4. Others (if you find anything else, please comment :) )

#!/usr/bin/perl -w
#===============================================================================
#
#         FILE:  mount_usb_disks.pl
#
#        USAGE:  ./mount_usb_disks.pl 
#
#  DESCRIPTION:  Mount USB disks or pendrive
#
#      OPTIONS:  ---
# REQUIREMENTS:  ---
#         BUGS:  ---
#        NOTES:  ---
#       AUTHOR:  Mitesh Singh Jat (mitesh), <mitesh[at]yahoo-inc[dot]com>
#      COMPANY:  Yahoo Inc, India
#      VERSION:  1.0
#      CREATED:  02/22/2012 05:37:54 PM IST
#     REVISION:  ---
#===============================================================================

use strict;
use warnings;

use Getopt::Std;
my $mount_point_base = "/mnt/usb_disks";
my $umount = 0;

sub usage()
{
    print STDERR "USAGE: $0 [options] \n";
    print STDERR "          -d <mount_base_dir>     mount point dir: default  $mount_point_base\n";
    print STDERR "          -u                      unmount \n";
}

sub run_cmd()
{
    my $cmd = $_[0];
    print "Running: $cmd\n";
    system("$cmd");
    my $retval = $?;
    if ($retval < 0)
    {
        print STDERR "Error in running: $cmd\n$retval\n";
    }
    return ($retval);
}

sub run_cmd_get()
{
    my $cmd = $_[0];
    print "Running: $cmd\n";
    my @outs = `$cmd`;
    my $retval = $?;
    if ($retval < 0)
    {
        print STDERR "Error in running: $cmd\n$retval\n";
        exit($retval);
    }
    chomp(@outs);
    return (@outs);
}

my %opts;
getopt('d:', \%opts);

foreach my $opt (sort keys %opts)
{
    if (!defined($opts{$opt}))
    {   
        print STDERR "$0: Requires value for option '$opt'\n";
        &usage();
        exit(-1);
    }   
}
if (defined($opts{"d"}))
{
    $mount_point_base = $opts{"d"};
}
if (defined($opts{"u"}))
{
    $umount = 1;
}

my $retval = 0;
my $cmd;
$retval = `whoami`;
chomp($retval);
unless ($retval eq "root")
{
    print STDERR "$0: Please run this script as 'root' user.\n";
    &usage();
    exit(-1);
}
unless ($umount)
{
    print "Mounting on $mount_point_base\n";
    unless (-d "$mount_point_base") 
    {
        $retval = &run_cmd("mkdir -p $mount_point_base");
        exit($retval) if ($retval < 0);
    }

    my @temps = &run_cmd_get("mount | awk '{print \$1}' | grep \"^/dev/\"");
    my %mounted_devs;
    foreach my $temp (@temps)
    {
        $mounted_devs{$temp} = 1;
        $temp =~ s/\/sd([a-z]).*/\/sd$1/;
        $mounted_devs{$temp}++;
    }
    ## Take care of swap
    @temps = &run_cmd_get("cat /proc/swaps | awk '{print \$1}' | grep \"^/dev/\"");
    foreach my $temp (@temps)
    {
        $mounted_devs{$temp} = 1;
        $temp =~ s/\/sd([a-z]).*/\/sd$1/;
        $mounted_devs{$temp}++;
    }
    my %unmounted_devs;
    my $total = 0;
    @temps = &run_cmd_get("ls /dev/sd*");
    foreach my $temp (@temps)
    {
        next if (defined($mounted_devs{$temp}));
        ## Take care of logical partition, which cannot be mounted
        my @outs = &run_cmd_get("fdisk -s $temp");
        $retval = $outs[0];
        next if ($retval <= 1);      
        $unmounted_devs{$temp} = 1;
        $temp =~ s/\/sd([a-z]).*/\/sd$1/;
        if (defined($unmounted_devs{$temp}))
        {
            delete($unmounted_devs{$temp});
            $total--;
        }
        $total++;
    }
    if ($total == 0)
    {
        print "$0: No partition to mount.\nexiting...\n";
        exit(0);
    }

    print "\n--------------------------------------------\n";
    print "Following partitions are not mounted...\n";
    print "--------------------------------------------\n";
    foreach my $temp (sort keys %unmounted_devs) 
    {
        print "$temp\n";
    }
    print "--------------------------------------------\n";

    ## Now we can mount these
    foreach my $temp (sort keys %unmounted_devs)
    {
        my $mount_dir = $temp;
        $mount_dir =~ s/^\/dev\//$mount_point_base\//;
        print "Mounting $temp -> $mount_dir\n";
        unless (-d "$mount_dir")
        {
            $retval = &run_cmd("mkdir -p $mount_dir");
            exit($retval) if ($retval < 0);
        }

        $cmd = "mount $temp $mount_dir";
        $retval = &run_cmd("$cmd");
        if ($retval < 0)
        {
            print STDERR "!!!Cannot mount $temp on $mount_dir\n";
        }
    }
}
else
{
    unless ($mount_point_base =~ m/^\//) 
    {
        $retval = `pwd`;
        chomp($retval);
        $mount_point_base = "$retval/$mount_point_base";
    }
    print "Unmounting from $mount_point_base\n";
    $cmd = "mount | awk -F \" on \" '{print \$2}' | awk -F \" type \" '{print \$1}' | grep \"$mount_point_base\"";
    my @temps = &run_cmd_get("$cmd");
    if (@temps == 0)
    {
        print "$0: No partition to unmount.\nexiting...\n";
        exit(0);
    }
    print "\n--------------------------------------------\n";
    print "Following directories are mounted...\n";
    print "--------------------------------------------\n";
    foreach my $temp (@temps)
    {
        print "$temp\n";
    }
    print "--------------------------------------------\n";

    foreach my $temp (@temps)
    {
        $cmd = "umount $temp";
        $retval = &run_cmd("$cmd");
        if ($retval < 0)
        {
            print STDERR "!!!Cannot umount $temp\n";
        }
        else
        {
            &run_cmd("rmdir $temp");       # delete dir if empty
        }
    }
}
exit(0);

Monday, February 13, 2012

Video Cutter V2: Video Cutting Using Mencoder

I have upgraded the Video Cutter program. Now it has 2 more features:
1. Strict Mode (-s flag): In this mode, strict monotonously increasing order of times must be given in the configuration (default: video_cutter.conf) file.
2. Joining Split Videos (-j flag): When this flag is given with the command, it does join the split video files into a single video file.

Here is the updated version of the earlier perl script:
#!/usr/bin/perl -w
#===============================================================================
#
#         FILE:  video_cutter.pl
#
#        USAGE:  ./video_cutter.pl [options] <input_video_file>
#
#  DESCRIPTION:  
#
#      OPTIONS:  ---
# REQUIREMENTS:  ---
#         BUGS:  ---
#        NOTES:  ---
#       AUTHOR:  Mitesh Singh Jat (mitesh), <mitesh[at]yahoo-inc[dot]com>
#      VERSION:  2.0
#      CREATED:  02/12/2011 03:57:55 PM IST
#===============================================================================

use strict;
use warnings;

use Getopt::Std;

sub usage()
{
    print STDERR "USAGE: $0 [options] <input_video_file>\n";
    print STDERR "          -c <conf_file>    default  /base_dir/input_video_file/video_cutter.conf\n";
    print STDERR "                            Start_time(hh:mm:ss),End_time(hh:mm:ss)\n\n";
    print STDERR "          -o <out_dir>      default  /base_dir/input_video_file/\n";
    print STDERR "          -s                strict format check for conf files     [default: no checking]\n";
    print STDERR "          -j                Join output splits into one video file [default: no joining]\n";
}

sub hms_to_seconds()
{
    my $end_sec = 0;
    my ($h, $m, $s) = split(/:/, $_[0]);
    $s = defined($s) ? $s : 0;
    $end_sec += $s;
    $m = defined($m) ? $m : 0;
    $end_sec += (60 * $m);
    $h = defined($h) ? $h : 0;
    $end_sec += (60 * 60 * $h);
    return($end_sec);
}

sub check_times_in_file()
{
    my $conf_file = $_[0];
    open(FH, "$conf_file") or die("$0: Cannot open '$conf_file'\n");
    my $line;
    my $nline = 0;
    my $prev_end_sec = -1;
    while ($line = <FH>)
    {
        chomp($line);
        ++$nline;
        next if ($line =~ m/^#/);
        my ($start_time, $end_time) = split(/,/, $line);
        if (!defined($end_time))
        {
            print STDERR "$0: End time(HH:MM:SS) is not present in line no $nline: $line\n";
            close(CFH);
            return;
        }
        my ($h, $m, $s) = split(/:/, $start_time);
        if (!defined($s))
        {
            print STDERR "$0: Start time is not in format (HH:MM:SS) line no $nline: $line\n";
            close(CFH);
            return;
        }
        ($h, $m, $s) = split(/:/, $end_time);
        if (!defined($s))
        {
            print STDERR "$0: End time is not in format (HH:MM:SS) line no $nline: $line\n";
            close(CFH);
            return;
        }

        my $start_sec = &hms_to_seconds($start_time);
        my $end_sec = &hms_to_seconds($end_time);
        if ($start_sec >= $end_sec)
        {
            print STDERR "$0: $start_sec >= $end_sec in line no $nline: $line\n";
            close(CFH);
            return;
        }
        if ($prev_end_sec >= $start_sec)
        {
            print STDERR "$0: $prev_end_sec >= $start_sec in line no $nline: $line and prev line\n";
            close(CFH);
            return;
        }
        $prev_end_sec = $end_sec;
    }
    close(FH);
    return ($nline);
}

my %opts;
getopt('o:c:', \%opts);

foreach my $opt (sort keys %opts)
{
    if (!defined($opts{$opt}))
    {
        print STDERR "$0: Requires value for option '$opt'\n";
        &usage();
        exit(-1);
    }
}

if (@ARGV != 1)
{
    &usage();
    exit(-1);
}

my $input_video_file = "$ARGV[0]";
my $out_dir = `dirname $input_video_file`;
chomp($out_dir);
my $conf_file = "$out_dir/video_cutter.conf";
my $strict_checking = 0;
my $join_outputs = 0;
my $out_video_files = "";

if (defined($opts{"c"}))
{
    $conf_file = $opts{"c"};
}
if (defined($opts{"o"}))
{
    $out_dir = $opts{"o"};
}
if (defined($opts{"s"}))
{
    $strict_checking = 1;
}
if (defined($opts{"j"}))
{
    $join_outputs = 1;
}

unless (-f "$input_video_file")
{
    print STDERR "$0: Input video file '$input_video_file' is not present\n";
    exit(-1);
}
unless (-f "$conf_file")
{
    print STDERR "$0: split conf file '$conf_file' is not present\n";
    exit(-1);
}
unless (-d "$out_dir")
{
    print STDERR "$0: out dir '$out_dir' is not present\n";
    exit(-1);
}
unless (-w "$out_dir")
{
    print STDERR "$0: out dir '$out_dir' is not writable\n";
    exit(-1);
}

my $mencoder = "/usr/local/bin/mencoder";
unless (-x $mencoder)
{
    $mencoder = "/usr/bin/mencoder";
}
unless (-x $mencoder)
{
    print STDERR "$0: please install mencoder\n";
    print STDERR "sudo apt-get install mencoder\n";
    exit(-1);
}

open(CFH, "$conf_file") or die("$0: Cannot open '$conf_file'\n");
my $line;
my $nline = 0;
my $nsplit = 0;
my $split_name = `basename $input_video_file`;
chomp($split_name);
$split_name =~ s/\.[^.]*$//;
my $split_ext = $input_video_file;
$split_ext =~ s/.*\.([^.]*)$/$1/;
my $success = 0;

if ($strict_checking == 1)
{
    print "Strict Mode\n";
    my $retval = &check_times_in_file($conf_file);
    if (!defined($retval))
    {
        print STDERR "$0: Problem in conf file \"$conf_file\"\n";
        exit(-1);
    }
}
while ($line = <CFH>)
{
    chomp($line);
    $nline++;
    next if ($line =~ m/^#/);
    my ($start_time, $end_time) = split(/,/, $line);
    next if (!defined($end_time));
    my $start_sec = &hms_to_seconds($start_time);
    my $end_sec = &hms_to_seconds($end_time);
    if ($start_sec >= $end_sec)
    {
        print STDERR "$0: $start_sec >= $end_sec\n";
        print STDERR "    $start_time >= $end_time ... skipping for line no $nline ...\n";
        next;
    }
    $end_sec -= $start_sec;
    my $cmd = sprintf("%s -ss %d -endpos %d -ovc copy -oac copy -o %s/%s_%03d.%s %s", 
            $mencoder, $start_sec, $end_sec, $out_dir, $split_name, $nsplit,
            $split_ext, $input_video_file);
    print "\n\n";
    print "-" x 80 . "\n";;
    print "$cmd\n";
    system("$cmd");
    if ($? != 0)
    {
        print STDERR "$0: failed to create $nsplit split for line no $nline\n";
        print STDERR "\t$cmd\n";
    }
    else
    {
        print STDOUT "Created $nsplit split for line $nline\n";
        $out_video_files .= sprintf(" %s/%s_%03d.%s", $out_dir, $split_name, $nsplit, $split_ext);
        $success++;
    }
    print "-" x 80 . "\n";;
    $nsplit++;
}

close(CFH);

print "\n";
print "=" x 80 . "\n";;
printf("Total lines = %d,   Success = %d/%d,  Failure = %d/%d\n",
        $nline, $success, $nsplit,
        $nsplit - $success, $nsplit);
print "=" x 80 . "\n";;

if ($join_outputs == 1)
{
    if ($success < 1)
    {
        print STDERR "$0: failed to join video files.\n";
        exit(-1);
    }
    my $joined_video_file = sprintf("%s/%s_joined.%s", $out_dir, $split_name, $split_ext);
    my $cmd = sprintf("%s -ovc copy -oac copy -o %s %s", 
            $mencoder, $joined_video_file, $out_video_files);
    print "\n\n";
    print "-" x 80 . "\n";;
    print "Joining Video Files into $joined_video_file\n";
    print "-" x 80 . "\n";;
    print "$cmd\n";
    system("$cmd");
    if ($? != 0)
    {
        print STDERR "$0: failed to create joined video file $joined_video_file\n";
        print STDERR "\t$cmd\n";
    }
    else
    {
        print STDOUT "Created joined video file $joined_video_file\n";
    }
    print "-" x 80 . "\n";;
}

exit(0);