Skip to content

Instantly share code, notes, and snippets.

@jbarrett
Created November 13, 2012 19:32
Show Gist options
  • Save jbarrett/4067848 to your computer and use it in GitHub Desktop.
Save jbarrett/4067848 to your computer and use it in GitHub Desktop.
Quick and dirty script to report on audio files encoded at less than a given bitrate
#!/usr/bin/env perl
use strict;
use warnings;
use v5.10.1;
use Image::ExifTool qw/:Public/;
use Getopt::Long;
use File::Which qw/which/;
use File::Find;
use Pod::Usage;
use IPC::Open3;
my $VERSION = "0.1";
my $EXIF_BIT_RE = '^(AudioBitrate|MaxBitrate|AvgBitrate|NominalBitrate)$';
my %opts = (
exts => 'ogg,mp3,wma,mpc,aac,m4a',
bits => 192,
dir => '',
mplayer => which('mplayer'),
verbose => 0
);
sub get_options {
GetOptions(
"e|exts=s" => \$opts{exts},
"d|dir=s" => \$opts{dir},
"m|mplayer=s" => \$opts{mplayer},
"b|bitrate=i" => \$opts{bits},
"h|help" => \$opts{help},
"v|verbose" => \$opts{verbose},
) or pod2usage(-verbose => 1) && exit;
pod2usage(-msg => 'FATAL: Directory not supplied', -verbose => 1) unless $opts{dir};
$opts{exts_re} = join ('|', map {qr/$_/} split (' *, *', $opts{exts}));
}
# Best effort attempt to get an audio file's bit rate.
# First attempt is to retrieve it from Image::ExifTool::ImageInfo
# Second attempt retrieves ID_AUDIO_BITRATE from mplayer -identify output
# Last ditch effort is to try get a rough result from (file size / track length)
sub low_bits {
my ($filename) = @_;
my $pid;
my $bitrate=0;
my $length=0;
my $size=0;
my ($out, $in, $err);
my $info = ImageInfo($filename);
for (keys %{$info}) {
when (/$EXIF_BIT_RE/) {
($bitrate = $info->{$_}) =~ s/[^0-9]//g;
if ($bitrate > 0 && $bitrate < $opts{bits}) {
print "($bitrate) " if $opts{verbose};
return 1;
}
}
}
if (!$bitrate && $opts{mplayer}) {
$pid = open3($out, $in, $err, $opts{mplayer}, '-frames', '0', '-identify', $filename);
waitpid($pid, 0);
for (<$in>) {
when (/ID_LENGTH=(.*)/) {
$length = $1;
}
when (/ID_AUDIO_BITRATE=(.*)/) {
$bitrate = ($1 / 1024);
if ($bitrate > 0 && $bitrate < $opts{bits}) {
print "($bitrate) " if $opts{verbose};
return 1;
}
}
}
if (!$bitrate && $length) {
$size = -s $filename;
if ($size) {
$bitrate = int((($size / $length) * 8) / 1024);
if ($bitrate > 0 && $bitrate < $opts{bits}) {
print "(EST. $bitrate) " if $opts{verbose};
return 1;
}
}
}
}
return 0;
}
sub wanted {
/($opts{exts_re})$/i || return;
print "$File::Find::name \n" if (low_bits($File::Find::name));
}
get_options();
find(\&wanted, $opts{dir});
=encoding utf8
=head1 NAME
bitrate_report
=head1 SYNOPSIS
bitrate_report -d music directory [OPTIONS]
=head1 DESCRIPTION
Find audio files which appear to be below a given average bitrate.
Recursively scan a given directory for audio files using lossy codecs, such as
Ogg Vorbis and MP3, then print a report on which files fall below a given
threshold bitrate to STDOUT.
We use a couple of mechanisms to discover the bitrate, but some files will
probably fall through the cracks.
=head1 ARGUMENTS
-d, --dir Directory containing music files.
-e, --ext Comma separated list of extensions to search for.
Default is ogg,mp3,wma,mpc,aac,m4a
-b, --bitrate Threshold bitrate. Any track falling below this bitrate is
reported.
Default is 192kbps.
-m, --mplayer Path to mplayer. Mplayer is used for Musepack files and as a
fallback when Image::ExifTool fails to extract metadata.
If mplayer is unavailable, no fallback is attempted.
Default is search in $PATH.
-v, --verbose Display detected bitrate alongside filename.
-h, --help Display this help message.
=head1 DEPENDENCIES
bitrate_report requires non-core modules Image::ExifTool and File::Which
and perl > v5.10.1
=head1 AUTHOR
John Barrett <johna.barrett@gmail.com>
=head1 CHARACTERISTICS
Quick and dirty.
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment