看這裏
#!/usr/bin/perl -w
# Filename: ogg2mp3
# Author: David Ljung Madison <DaveSource.com>
# See License: http://MarginalHacks.com/License
# Description: Converts ogg to mp3, with artist/title/info
# Ideas from: Joseph E. O'Doherty <odoherty.net>
# id3v2 from: Javier Salazar Loyola <jsalazar cern ch>
use strict;
use File::Spec;
##################################################
# Setup the variables
##################################################
my $PROGNAME = $0;
$PROGNAME =~ s|.*/||;
my $TMPFILE = File::Spec->catfile( File::Spec->tmpdir(), "$PROGNAME.$$.wav" );
#########################
# Ogg decoder
#########################
# Different versions of ogg123 handle files differently! Argh!
#my $OGGDEC = "ogg123 -d wav -o file:$TMPFILE";
#my $OGGDEC = "ogg123 -d wav -f $TMPFILE";
# Just use oggdec instead.
# Add -Q for quiet..
my $OGGDEC = "oggdec -o $TMPFILE";
#########################
# MP3 encoder
# Choose your favorite..
#########################
my $MP3ENC = "lame";
#my $MP3ENC = "notlame";
my $OGGINFO = "ogginfo";
my $ID3TOOL = "id3tool";
my $ID3V2 = "id3v2";
##################################################
# Usage
##################################################
sub usage {
foreach my $msg (@_) { print STDERR "ERROR: $msg\n"; }
print <<USAGE;
Usage:\t$PROGNAME [-d] [-o <dir>] [<playlist.m3u> | <ogg> ..]
Converts ogg to mp3
-d Set debug mode
-o <dir> Specify output directory
-id3v2 Use id3v2 instead of id3tool for tagging
-keepdir Keep directory structure inside specified -o directory
-f Force rewriting of mp3 if it already exists
--enc_opts .. -- Options for encoding tool
--dec_opts .. -- Options for decoding tool
Example: $PROGNAME --enc_opts -b 64 -- *.ogg
USAGE
exit -1;
}
sub get_dashdash {
my @dash;
while ($#ARGV>=0) {
my $arg=shift(@ARGV);
last if $arg eq "--";
push(@dash,$arg);
}
@dash;
}
sub contents {
my ($f) = @_;
open(F,"<$f") || usage("Couldn't read playlist [$f]");
my @f = <F>;
close F;
chomp @f;
@f;
}
sub parse_args {
my $opt = {};
my $outdir;
my @oggs;
while (my $arg=shift(@ARGV)) {
if ($arg =~ /^-h$/) { usage(); }
if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; }
if ($arg =~ /^-o$/) { $opt->{out}=shift @ARGV; next; }
if ($arg =~ /^-id3v2$/) { $opt->{id3v2}=1; next; }
if ($arg =~ /^-keepdir$/) { $opt->{keepdir}=1; next; }
if ($arg =~ /^-f$/) { $opt->{force}=1; next; }
if ($arg =~ /^--enc_opts$/) { push(@{$opt->{enc_opts}}, get_dashdash()); next; }
if ($arg =~ /^--dec_opts$/) { push(@{$opt->{dec_opts}}, get_dashdash()); next; }
if ($arg =~ /^-/) { usage("Unknown option: $arg"); }
push(@oggs, ($arg =~ /\.m3u$/) ? contents($arg) : $arg);
}
usage("No oggs specified") unless @oggs;
($opt,@oggs);
}
sub debug {
return unless $MAIN::DEBUG;
foreach my $msg (@_) { print STDERR "[$PROGNAME] $msg\n"; }
}
##################################################
# Genre code
##################################################
my @GENRES;
sub get_genres() {
return if @GENRES;
open(G,"$ID3TOOL -l|") || die("[$PROGNAME] Can't get genre info: [$ID3TOOL -l]\n");
while (<G>) {
push(@GENRES,$1) if (/^(\S.*\S)\s+\|/);
}
close G;
}
sub get_genre {
my ($genre) = @_;
return undef unless $genre;
get_genres;
my @g = grep(/^$genre$/i, @GENRES);
return $g[0] if @g;
@g = grep(/$genre/i, @GENRES);
print STDERR "[$PROGNAME] Multiple genre matches [$genre -> @g]\n" if $#g>0;
return $g[0] if @g;
# No match
print STDERR "[$PROGNAME] Unknown genre [$genre]\n";
return undef;
}
##################################################
# Main code
##################################################
sub ogginfo {
my ($ogg) = @_;
# Get ogg tags
open(TAGS,"$OGGINFO \Q$ogg\E |") || die("[$PROGNAME] Couldn't run ogginfo [$ogg]\n");
my %i;
while(<TAGS>) {
chomp;
next if /^\s*$/;
## The old way:
# die("[$PROGNAME] Bad ogginfo: [$_]") unless /^\s*(\S+)=(.*)$/;
# $i{$1} .= $2;
## Old ogginfo:
## "key=value"
##
## New ogginfo:
## "Some comment stuff..."
## " key=value"
##
## I suppose I could parse the entire output to see if it's new or
## old, but it looks like the comments never have '=' in them, and who
## cares if it does anyways? So, we'll just use a loose regexp:
if (/^\s*(\S+)=(.*)$/) {
my ($tag,$val) = (lc($1),$2);
$i{$tag} .= $val
}
}
close(TAGS);
\%i;
}
sub dir {
my ($path) = @_;
return undef unless $path =~ m|/|;
$path =~ s|/+$||;
$path =~ s|/[^/]+$||;
$path;
}
sub mkdirOf {
my ($path) = @_;
return if !defined $path || -d $path;
mkdirOf(dir($path));
mkdir($path)
}
sub set_mp3info {
my ($opt,$mp3,$info) = @_;
my $tool = $opt->{id3v2} ? $ID3V2 : $ID3TOOL;
# Flags that are different
my $trackFlag = $opt->{id3v2} ? 'T' : 'c';
my $albumFlag = $opt->{id3v2} ? 'A' : 'a';
my $artistFlag = $opt->{id3v2} ? 'a' : 'r';
my $commentFlag = $opt->{id3v2} ? 'c' : 'n';
my $genreFlag = $opt->{id3v2} ? 'g' : 'G';
my $legitDate = $opt->{id3v2} ? '^[\d+-\/]$' : '^\d+$';
my $set;
# Newer id3tool supports setting track
#$set .= " -n \QTrack $info->{tracknumber}\E" if $info->{tracknumber} && !$info->{comment};
$set .= " -$trackFlag \Q$info->{tracknumber}\E" if $info->{tracknumber};
$set .= " -t \Q$info->{title}\E" if $info->{title};
$set .= " -$albumFlag \Q$info->{album}\E" if $info->{album};
$set .= " -$artistFlag \Q$info->{artist}\E" if $info->{artist};
$set .= " -$commentFlag \Q$info->{comment}\E" if $info->{comment};
my $genre = $opt->{id3v2} ? $info->{genre} : get_genre($info->{genre});
$set .= " -$genreFlag \Q$genre\E" if $genre;
$set .= " -y \Q$info->{date}\E" if $info->{date} && $info->{date} =~ /$legitDate/;
return print STDERR "[$PROGNAME] No tag info for [$mp3]\n" unless $set;
system("$tool $set \Q$mp3\E");
print STDERR "[$PROGNAME] Errors from:\n $tool $set $mp3\n $!\n" if $?;
}
sub ogg2mp3 {
my ($opt,$ogg) = @_;
my $mp3 = $ogg;
$mp3 =~ s/(\.ogg)?$/.mp3/i;
# Handle outdir if specified
if ($opt->{out}) {
$mp3 =~ s|.*/||g unless $opt->{keepdir};
$mp3 = "$opt->{out}/$mp3";
}
return print STDERR "[$PROGNAME] Skipping mp3 (already exists) [$mp3]\n"
if !$opt->{force} && -f $mp3;
# Make sure the directory exists
mkdirOf(dir($mp3));
my $dec_opts = $opt->{dec_opts} ? join(' ',@{$opt->{dec_opts}}) : "";
my $enc_opts = $opt->{enc_opts} ? join(' ',@{$opt->{enc_opts}}) : "";
print "$ogg -> $mp3\n";
# Decode
# system("$OGGDEC \Q$ogg\E | $MP3ENC - \Q$mp3\E");
# print STDERR "[$PROGNAME] Errors from:\n $OGGDEC \Q$ogg\E | $MP3ENC - \Q$mp3\E\n $!\n"
# if $?;
system("$OGGDEC $dec_opts \Q$ogg\E");
print STDERR "[$PROGNAME] Errors from:\n $OGGDEC \Q$ogg\E\n $!\n" if $?;
# Encode
system("$MP3ENC $enc_opts $TMPFILE \Q$mp3\E");
print STDERR "[$PROGNAME] Errors from:\n $MP3ENC $TMPFILE \Q$mp3\E\n $!\n" if $?;
unlink $TMPFILE;
my $info = ogginfo($ogg);
set_mp3info($opt,$mp3,$info);
}
sub main {
my ($opt,@oggs) = parse_args();
map(ogg2mp3($opt,$_), @oggs);
}
main();