#! /usr/bin/perl -w
# Program: Oggasm
# Version: 1.4.0
# Created: 4/20/01
# Last Modified: 2/17/02
# Written By: Sean Kellogg
# Lincensed under the GPL
# Modules
# -------
use MP3::Info;
use File::Copy;
# Intro and Startup Variables
# ---------------------------
print "\nOggasm v 1.4.\nThis program is licensed under the GPL and as such comes with NO WARRANTY!!!\n";
my %user; # this hash stores all the important data
$date = localtime;
# Start Log
# ---------
open REPORT, ">>$ENV{HOME}/oggasm.$date.log" or die "can't create log file: $!\n";
print REPORT "Oggasm Report Log: $date\n";
# Process command-line flags
# --------------------------
&checkflags(@ARGV);
# ask users questions about task
# ------------------------------
&getuserdata;
# create destination directory
# ----------------------------
print REPORT "\n--FOLDER CREATION--\n";
&createdestinationdir($user{'source'},$user{'destination'}) if ($user{'option'} =~ /3|4/);
print "file hierarchy created successfully!!\n" if ($user{'option'} =~ /3|4/);
print "no file hierachry required\n" if ($user{'option'} =~ /1|2/);
# begin file conversion
# ---------------------
print "Beginning Conversion...\n";
print REPORT "\n--FILE CONVERSION--\n";
&convertfile($user{'source'},$user{'destination'});
# finish log
# ----------
print REPORT "Program exited successfully\n\n";
close REPORT;
# terminate oggasm
# ----------------
print "A log of oggasm's activies can be found in $ENV{HOME}/oggasm.log\n";
print "Oggasm has exited succesfully\n\n";
exit;
# FUNCTIONS
# createdestinationdir
# ------------
# Recursive function that takes a folder to start copying from and a folder name to start creating at.
# The function will step through each directory and create it in same relative location as found in
# the starting directory.
sub createdestinationdir{
my ($here,$there) = @_;
chomp $there if $there=~/\//;
chomp $here if $here=~/\//;
$| = 1;
print "Creating folder $there - ";
if (!(-d $there)){
print REPORT "Creating folder $there - ";
mkdir($there) or die "Unable to create $there, you probably don't have write permissions\n";
} else {
print REPORT "Folder $there already exists - ";
}
print "Success!\n";
print REPORT "success\n";
$| = 0;
opendir(DIR, $here) || die "can't opendir $here: $!";
my @contents = readdir(DIR);
closedir DIR;
foreach (@contents){
if (!($_=~/^\./)){
if (-d "$here/$_"){
if ("$here/$_" eq $user{'destination'}){
print REPORT "Destination folder located - skipping\n";
} else {
createdestinationdir("$here/$_","$there/$_");
}
}
}
}
}
# convertfile
# -----------
# A recursive function that takes a directory to convert from and a directory to create at. Note: the
# directories may be the same. The function goes through the directory contents and stops at each file
# ending with the mp3 extension. It will try to determine the artist and title from the ID3 tag, but
# will try and parse the file name if the ID3 Tag is unavailable. The conversion is made via a system call.
# If the option is set, the mp3 will be deleted after conversion. If the function encounters a directory, it
# will call itself passing the new folder. If the option is set, the function will delete the folder
# once it has converted all the mp3s and no files are left in the folder.
sub convertfile{
my ($here,$there) = @_;
chomp $there if $there=~/\//;
chomp $here if $here=~/\//;
opendir(DIR, $here) || die "can't opendir $here: $!";
my @contents = readdir(DIR);
closedir DIR;
foreach (@contents){
if (!($_=~/^\./)){
if (-d "$here/$_"){
&convertfile("$here/$_","$there/$_");
}
if($_=~/\.$user{'convertfrom'}/){
my $from = $_;
my $to = $1 if ($from =~ /(.*)\./);
my $fileName = $1;
$to .= ".$user{'convertto'}";
print "$from >> $to\n";
my $title;
my $artist;
my $album;
my $tracknum;
if ($user{'convertfrom'} eq "mp3"){
my $tag = get_mp3tag("$here/$from") ;
$title = $tag->{TITLE};
$artist = $tag->{ARTIST};
$album = $tag->{ALBUM};
$tracknum = $tag->{TRACKNUM};
}
# Step through the @format looking for metacharacters. use regex to parse out the string
for (my $i=1; $i < $#format; $i = $i+2){
if ($format[$i] eq '%a' && !($artist)){
my $pre = $format[$i-1];
my $post = $format[$i+1];
if ($post){
$fileName=~/$pre(.*?)$post/;
$artist = $1;
}else{
$fileName=~/$pre(.*)$post/;
$artist = $1;
}
print REPORT "grabed artist name from file: $artist\n";
}
if ($format[$i] eq '%t' && !($title)){
$pre = $format[$i-1];
$post = $format[$i+1];
if ($post){
$fileName=~/$pre(.*?)$post/;
$title = $1;
}else{
$fileName=~/$pre(.*)$post/;
$title = $1;
}
print REPORT "grabed title name from file: $title\n";
}
if ($format[$i] eq '%n' && !($tracknum)){
$pre = $format[$i-1];
$post = $format[$i+1];
if ($post){
$fileName=~/$pre(.*?)$post/;
$tracknum = $1;
}else{
$fileName=~/$pre(.*)$post/;
$tracknum = $1;
}
print REPORT "grabed track number from file: $tracknum\n";
}
if ($format[$i] eq '%l' && !($album)){
$pre = $format[$i-1];
$post = $format[$i+1];
if ($post){
$fileName=~/$pre(.*?)$post/;
$album = $1;
}else{
$fileName=~/$pre(.*)$post/;
$album = $1;
}
print REPORT "grabed album name from file: $album\n";
}
}
# BEHOLD!!! THE MOTHER COMMAND!!
# mpg123 sends out raw PCM data to standardout in quite mode, errors are sent to /dev/null.
# The PCM data is piped to oggenc which has been niced. oggenc is running in quite mode,
# and excepting data via standard in. the letter flags give instructions for the comment tag.
# The last dash indicates using standard in.
if ($user{'convertto'} eq "ogg"){
$signal = system("mpg123 -sq \"$here/$from\" | nice -n \"$user{'nice'}\" oggenc -Q -r -b \"$user{'bitrate'}\" -o \"$there/$to\" -a \"$artist\" -t \"$title\" -N \"$tracknum\" -l \"$album\" -c \"INFO: Coverted by Oggasm 1.4.0\" - ");
&checkterminate("$signal");
} elsif ($user{'convertto'} eq "ogg") {
$signal = system("ogg123 -d wav -q \"$here/$from\"");
&checkterminate("$signal");
$signal = system("nice -n \"$user{'nice'}\" lame -S -b \"$user{'bitrate'}\" --ta \"$artist\" --tt \"$title\" --tn \"$tracknum\" --tl \"$album\" output.wav \"$there/$to\" 2> /dev/null");
&checkterminate("$signal");
unlink ("output.wav");
} else {
print "hmm... oggasm doesn't know if your running in reverse mode or not. That's both very odd and very bad.\nTerminating Oggasm\n";
exit;
}
if (-s "$there/$to"){
print REPORT "Converting $here/$from >> $there/$to: success\n";
unlink ("$here/$from") if ($user{'option'} =~ /2|4/);
}else{
print REPORT "Converting $here/$from >> $there/$to: failure\n";
}
}
}
}
if ($user{'option'} =~ /4/){
rmdir($here);
}
}
sub checkterminate{
my ($signal) = @_;
if (!($signal == 0)){
print "User has terminated oggasm.\n";
exit;
}
}
sub getuserdata{
&confirmapps;
$user{'option'} = &setoption;
$user{'source'} = &setlocal("source");
if ($user{'option'} == 1 or $user{'option'} == 2){
$user{'destination'} = $user{'source'};
} else {
$user{'destination'} = &setlocal("destination");
}
$user{'bitrate'} = &setbitrate;
$user{'nice'} = &setnice;
@format = &titleformat;
&confirm;
}
sub confirm{
print "
Confirm Action:
---------------
Convert all $user{'convertfrom'}s found in $user{'source'} to $user{'convertto'}s and save them in
$user{'destination'}. Convert at $user{'bitrate'} kbs with a niceness of $user{'nice'}.\n\n";
print "DO NOT " if ($user{'option'} == 1 or $user{'option'} == 3);
print "DELETE ALL \U$user{'convertfrom'}s\E AFTER CONVERSION!!!\n";
print "------------------------------------------\n";
print "Yes[y]/Re-enter{r]/Quit[q]: ";
chomp (my $input = <STDIN>);
exit if ($input =~ /[Qq]/);
&getuserdata if ($input =~ /[Rr]/);
print "invalid option!\n" if (!($input =~ /[Yy]/));
&confirm if (!($input =~ /[Yy]/));
}
sub confirmapps{
# need to find a sutable free mp3 encoder
$user{'lame'} = &checkprog("lame");
$user{'oggenc'} = &checkprog("oggenc");
$user{'ogg123'} = &checkprog("ogg123");
$user{'mpg321'} = &checkprog("mpg321");
if ($user{'mpg321'} eq "false" and $user{'convertto'} eq "ogg"){
print "Oggasm requires mpg321 to run\n\n";
exit;
}
if ($user{'oggenc'} eq "false" and $user{'convertto'} eq "ogg"){
print "Oggasm requires oggenc to run\n\n";
exit;
}
if ($user{'ogg123'} eq "false" and $user{'convertto'} eq "mp3"){
print "Oggasm requires ogg123 to convert oggs into mp3s\n\n";
exit;
}
if ($user{'lame'} eq "false" and $user{'convertto'} eq "mp3"){
print "Oggasm requires lame to convert oggs into mp3s\n\n";
exit;
}
}
# checkprogs
# ----------
# check to see if a program is installed on the system
sub checkprog{
($prog) = @_;
my $check = system("which $prog > /dev/null");
return "true" if ($check eq '0');
return "false" if ($check);
}
# setoption
# ---------
# Asks user to chose between the 4 different run options. Runs additional functions as necessary and
# sets the global $option.
sub setoption{
print "
1: place $user{'convertto'}s in $user{'convertfrom'} folder - DO NOT DELETE \U$user{'convertfrom'}s\E
2: place $user{'convertto'}s in $user{'convertfrom'} folder - DELETE \U$user{'convertfrom'}s\E
3: place $user{'convertto'}s in separate $user{'convertto'} folder - DO NOT DELETE \U$user{'convertfrom'}s\E
4: place $user{'convertto'}s in separate $user{'convertto'} folder - DELETE \U$user{'convertfrom'}s\E
---------------------------------------------------------
Selection an option: ";
chomp (my $option = <STDIN>);
return $option;
}
# requestlocal
# ------------
# asks for either the source directory or destination directory. If source, it will check if the directory
# is there and ask for a new one if not.
sub setlocal{
($what) = @_;
my $root;
if ($what eq "source"){
print "Enter $user{'convertfrom'} directory to be converted: ";
chomp ($root = <STDIN>);
if (!(-d $root)){
print "$root is not a valid directory!!\n";
$root = &setlocal("source");
}
}
if ($what eq "destination"){
print "Enter $user{'convertto'} directory: ";
chomp ($root = <STDIN>);
}
if ($root =~ /\/$/){
chop($root);
}
return $root;
}
# setbitrate
# ----------
# asks user for a bitrate. Uses 128 if there is no user input. The function checks if the bitrate
# is to high or low, but doesn't bother to check if the value is one of the 6 standard because oggenc
# will approximate;
sub setbitrate{
print "
The encoder can encode at an average bitrate of:
112, 128, 160, 192, 256, or 350.
------------------------------------------------\n";
print "Enter desired bitrate or press enter for default [128]: ";
chomp (my $bitrate = <STDIN>);
$bitrate = 128 if !$bitrate;
if (112 > $bitrate or 350 < $bitrate){
print "$bitrate is not with the valid range!!\n";
$bitrate = &setbitrate;
}
return $bitrate;
}
# setnice
# -------
# asks the user for a niceness value. Checks if it is too high, too low, or a decimal.
sub setnice{
print "
The encoder can run at different levels of niceness, which
will determine how much of the system resources it will consume.
Niceness can be an integer between 0 (runs with everything else)
and 20 (everything else takes precendence)
---------------------------------------------------------------\n";
print "Enter desired niceness or press enter for default [0]: ";
chomp (my $nice = <STDIN>);
$nice = 0 if !$nice;
if ($nice < -20 or $nice > 20 or $nice=~/\./){
print "$nice is not a valid nice setting!!\n";
$nice = &setnice;
}
return $nice;
}
# titleformat
# -----------
# prompts use to enter format style for thier mp3 file names so the program can parse out the title
# and artist incase the ID3 tag is gone. Uses meta tags: %a=artist, %n=track number, %b=album, %t=title
# NOTE: if a user enters in characters used for regexing they will need to be escaped.
sub titleformat{
print "
In case your $user{'convertfrom'}s do not have valid ID tags, Oggasm
will try and parse the file name to set the $user{'convertto'} tag.
If your format is like the default, press enter, otherwise enter
your style using the meta charcters
------------------------------------------------------------------\n";
print 'Meta Characters: %a: artist %t: title %n: track number %b: album',"\n";
print 'Posible Example: (%n) - (%a) - (%t)',"\n";
print "------------------------------------------------------------------\n\n";
print "Enter your format or just press enter for default[\%a - \%t]: ";
chomp (my $responce = <STDIN>);
$responce = '%a - %t' if !($responce);
my $i = 0;
my @format;
# split the user input into sections devided by the meta charcters. This will be used to search the file
# name strings later.
while ($responce=~/%[atnb]{1}/){
$format[$i] = $`;
$format[$i+1] = $&;
$responce = $';
$i = ($i+2);
}
$format[$i] = $responce;
# Escape the characters that are used in regexing
foreach (@format){
$_=~s/\(/\\\(/g;
$_=~s/\)/\\\)/g;
$_=~s/\[/\\\[/g;
$_=~s/\]/\\\]/g;
$_=~s/\*/\\\*/g;
$_=~s/\$/\\\$/g;
$_=~s/\./\\\./g;
}
@_ = @format;
}
# checkflags
# ----------
# takes all arguments passed in by through the command lind and checks them with regular
# expressions. Adjusts user imput accordingly
sub checkflags {
@ARGV = @_;
$user{'convertto'} = "ogg"; #can be changed commandline option ONLY
$user{'convertfrom'} = "mp3"; #can be changed commandline option ONLY
foreach (@ARGV){
# set the script to convert all mp3s to oggs
if ($_ =~ /([reverse])/){
$user{'convertto'} = "mp3"; #can be changed commandline option ONLY
$user{'convertfrom'} = "ogg"; #can be changed commandline option ONLY
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1