From: epg <> Date: Wed, 27 Oct 2004 04:54:16 +0000 (+0000) Subject: Collapse fa-rip and fa-tags into a single Perl program, fa-rip. X-Git-Url: https://diplodocus.org/git/flac-archive/commitdiff_plain/c47f24178a0ff695262801bf9ca9b0efc9fa56e6 Collapse fa-rip and fa-tags into a single Perl program, fa-rip. Fix up the documentation. --- diff --git a/GNUmakefile b/GNUmakefile index beeb953..4eddf3d 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -19,11 +19,11 @@ ROFF= nroff -Tascii -mandoc SCRIPTS=\ fa-flacd \ fa-rip \ - fa-tags \ flac2mp3 \ MAN=\ fa-flacd.1 fa-flacd.0 \ + fa-rip.1 fa-rip.0 \ flac2mp3.1 flac2mp3.0 \ all: ${SCRIPTS} ${MAN} @@ -51,12 +51,6 @@ ${DESTDIR}${man1dir}/%: % ${DESTDIR}${man1dir} ${DESTDIR}${cat1dir}/%: % ${DESTDIR}${cat1dir} ${INSTALL_DATA} $< $@ -${DESTDIR}${man1dir}/fa-rip.1 ${DESTDIR}${man1dir}/fa-tags.1: - ln -sf fa-flacd.1 $@ - -${DESTDIR}${cat1dir}/fa-rip.0 ${DESTDIR}${cat1dir}/fa-tags.0: - ln -sf fa-flacd.0 $@ - ${DESTDIR}${bindir}: ${INSTALL_DIRECTORY} $@ diff --git a/README b/README index ebdadea..953a17d 100644 --- a/README +++ b/README @@ -6,10 +6,7 @@ flac-archive requires: Perl >= 5.6 MusicBrainz::Client - zsh >= 4.0, ksh93, or bash cdparanoia mkcue (http://diplodocus.org/projects/audio/) - mktemp -d (non-standard, but available on NetBSD, Mac OS X, - GNU/Linux, and probably just about everywhere else) flac2mp3 additionally requires lame. diff --git a/fa-flacd b/fa-flacd index 5f3ab7f..2cc1210 100755 --- a/fa-flacd +++ b/fa-flacd @@ -4,16 +4,12 @@ =head1 NAME -B, B, B - archive CDs to single FLAC files +B - archive CDs to single FLAC files =head1 SYNOPSIS B [B<-jv>] -B [B<-d> I] [B<-t> I] - -B I - =cut use strict; @@ -212,29 +208,23 @@ __END__ =head1 DESCRIPTION -B, B, and B together comprise -B, a system for archiving audio CDs to single FLAC -files. B is the guts of the system. It runs in the -directory where the audio archives are stored, scanning for new CDs to -encode and rename; it never exits. B generates the inputs for -B: the ripped WAV file, Vorbis tags, and a cuesheet. -B is not meant to be run directly; B uses it to -generate the candidate Vorbis tags. - -All three programs expect to be run from the same directory. They use -that directory to manage directories named by artist and by disc ID. -Intermediate files are written to the disc ID directory. B -processes the disc ID directories into per-album files in the artist -directories. - -=head2 FA-FLACD - -B does not exit; it runs until the user kills it. Every 5 -seconds it scans its current directory for directories with a file -called "tags" and creates a processing job for each one. The number -of jobs B attempts to run is controlled by the B<-j> option -and defaults to 4. B will print diagnostic output when the -B<-v> option is given. +B and B together comprise B, a system +for archiving audio CDs to single FLAC files. B is the guts +of the system. It runs in the directory where the audio archives are +stored, scanning for new ripped CDs to encode and rename; it never +exits. B generates the inputs for B: the ripped WAV +file, Vorbis tags, and a cuesheet. + +Both programs expect to be run from the same directory. They use that +directory to manage directories named by artist. Intermediate files +are written to temporary directories here. B processes the +temporary directories into per-album files in the artist directories. + +Every 5 seconds, B scans its current directory for +directories with a file called "tags" and creates a processing job for +each one. The number of jobs B attempts to run is +controlled by the B<-j> option and defaults to 4. B will +print diagnostic output when the B<-v> option is given. A processing job first renames the directory's "tags" file to "using-tags" so that B will not try to start another job for @@ -243,49 +233,9 @@ so a new job will not be started until the user corrects the error condition and renames "using-tags" back to "tags". Next, it encodes the "wav" file to a FLAC file, using the "cue" file for the cuesheet and "using-tags" for Vorbis tags. Any diagnostic output is saved in -the "log" file. Finally, the "cue" and "log" files are moved to the -artist directory (and named by album) and the ID directory is removed. - -=head2 FA-RIP - -B uses C to create a directory for storage of its -intermediate files. It uses C to create the "cue" file and -then passes the number of tracks (from the "cue" file) as command-line -arguments to B. - -If B<-t> I is specified, that number is used instead of -counting tracks in the "cue" file, and is also passed to C -to ensure that only that number of tracks is listed in the "cue" file. -This is handy if the CD has stupid data tracks. - -Finally, it execs C to rip the CD to the "wav" file. - -In order for this CD to be processed by B, the user must -create a "tags" file. This is usually done by renaming one of the -candidate-tags files and deleting the others. - -=head2 FA-TAGS - -B uses C to populate candidate-tags -files. These are numbered in the order of entries read from -MusicBrainz, e.g. candidate-tags-1, candidate-tags-2, etc. B -also creates candidate-tags-0, which has the correct fields for this -CD (including correct number of TITLE= lines), but with all fields -blank. - -B requires the number of tracks as its sole argument. - -=head1 ENVIRONMENT - -=over 4 - -=item CDDEV - -B uses this to rip audio and save the cuesheet for a CD. It -makes some effort to check some common device names for FreeBSD, -Linux, and NetBSD by default. - -=back +the "log" file. Finally, B moves the "cue" and "log" files +to the artist directory (named by album) and removes the temporary +directory. =head1 AUTHORS diff --git a/fa-rip b/fa-rip index 4755822..252c2b5 100755 --- a/fa-rip +++ b/fa-rip @@ -1,60 +1,227 @@ -#! /usr/bin/env zsh +#! /usr/bin/env perl # $Id$ +# $URL$ -set -e +=head1 NAME -function get_cddev { - typeset raw - raw=$(sysctl -n kern.rawpartition > /dev/null | awk '{printf "%c",97+$0}') +B - rip a CD for B - if [[ -n ${ZSH_VERSION} ]]; then - setopt NULL_GLOB - fi +=head1 SYNOPSIS - for CDDEV in /dev/{cdroms/cdrom*,cdrom*,rcd*${raw},{a,}cd*c}; do - [[ -e ${CDDEV} ]] && return 0 - done +B [B<-d> I] [B<-t> I] - return 1 +=cut + +use strict; +use warnings; + +use Env qw( + CDDEV +); + +use File::Temp; +use Getopt::Long; +use POSIX ':sys_wait_h'; +use Pod::Usage; + +use MusicBrainz::Client::Simple; + +sub run_or_die { + my $command = shift; + my $status; + + system($command); + + if (WIFEXITED($?)) { + $status = WEXITSTATUS($?); + if ($status != 0) { + die("$command exited $status"); + } + + } elsif (WIFSIGNALED($?)) { + $status = WTERMSIG($?); + die("$command signalled $status"); + + } elsif (WIFSTOPPED($?)) { + $status = WSTOPSIG($?); + die("$command stopped $status"); + + } else { + die("Major horkage on system($command): \$? = $? \$! = $!"); + } +} + +sub mkcue { + my $device = shift; + my $trackcount = shift; + my @command; + + push(@command, 'mkcue'); + + if (defined($trackcount)) { + push(@command, "-t $trackcount"); + } + + if (defined($device)) { + push(@command, $device); + } + + push(@command, '> cue'); + run_or_die(join(' ', @command)); + + if (not defined($trackcount)) { + open(F, 'cue') or die("open(cue): $!"); + $trackcount = grep(/TRACK.*AUDIO/, ); + close(F); + } + + return $trackcount; +} + +sub tags { + my $device = shift; + my $trackcount = shift; + my $mb; + my @results; + my $album; + my $i; + my @tracks; + my $name; + my $track; + my $j; + + if (defined($device)) { + $mb = new MusicBrainz::Client::Simple (device=>$device); + } else { + $mb = new MusicBrainz::Client::Simple; + } + + @results = $mb->lookup_cd; + if (not $mb->success) { + die($mb->get_error); + } + + open(F, '>candidate-tags-0') or die("open('>candidate-tags-0'): $!"); + print(F "$_=\n") for ('ARTIST', 'ALBUM', 'DATE'); + print(F "TITLE=\n") for 1 .. $trackcount; + close(F) or die("close('>candidate-tags-0'): $!"); + + for $album (@results) { + $i++; + open(F, ">candidate-tags-$i") or die("open(>candidate-tags-$i): $!"); + + print(F 'ARTIST=', $album->get_artist->get_name, "\n"); + print(F 'ALBUM=', $album->get_name, "\n"); + + # MusicBrainz doesn't have dates yet; these are usually wrong anyway. + print(F "DATE=\n"); + + @tracks = $album->get_tracks; + for $j (1 .. $trackcount) { + if ($track = shift(@tracks)) { + $name = $track->get_name; + } else { + $name = ''; + } + print(F "TITLE=$name\n"); + } + + close(F) or die("close(>candidate-tags-$i): $!"); + } +} + +sub rip { + my $device = shift; + my $trackcount = shift; + + $device ||= '/dev/cdrom'; + + print"$_\n" for ('cdparanoia', '-d', $device, "1-$trackcount", 'wav'); + exec('cdparanoia', '-d', $device, "1-$trackcount", 'wav'); + # exec prints its own error message so just + die; +} + +MAIN: { + my $trackcount; + my $help; + my $tempdir; + + GetOptions( + 'device|d=s' => \$CDDEV, + 'tracks|t=i' => \$trackcount, + 'help|h|?' => \$help, + ) or pod2usage(); + $help and pod2usage(-exitstatus=>0, -verbose=>1); + + # File::Temp::tempdir calls die on error. + $tempdir = File::Temp::tempdir('flac-archive.XXXXXXXXXX'); + chdir($tempdir) or die("chdir($tempdir): $!"); + + $trackcount = mkcue($CDDEV, $trackcount); + tags($CDDEV, $trackcount); + rip($CDDEV, $trackcount); } -trackcount= -while getopts 'd:t:' ch; do - case ${ch} in - d) - CDDEV=${OPTARG} - export CDDEV - ;; - t) - trackcount=${OPTARG} - ;; - ?) - echo 'fa-rip [-t track-count]' >&2 - exit 2 - ;; - esac -done -shift $((${OPTIND} - 1)) - -if [[ -z ${CDDEV} ]]; then - if ! get_cddev; then - echo 'CDDEV environment variable not set, defaults did not work' >&2 - exit 2 - fi - export CDDEV -fi - -dir=$(mktemp -d flac-archive.XXXXXXXXXX) -cd ${dir} - -if [[ -z ${trackcount} ]]; then - mkcue ${CDDEV} > cue - trackcount=$(grep -c 'TRACK.*AUDIO' cue) -else - mkcue -t ${trackcount} ${CDDEV} > cue -fi - -fa-tags ${trackcount} - -exec cdparanoia -d ${CDDEV} 1-${trackcount} wav + +__END__ + +=head1 DESCRIPTION + +B creates a temporary directory for storage of its +intermediate files, runs C to create the "cue" file, uses +MusicBrainz to generate candidate tags files, and runs +C to rip the CD to the "wav" file. + +In order for this CD to be processed by B, you must create a +"tags" file. This is usually done by renaming one of the +candidate-tags files and deleting the others. Don't forget to fill in +the DATE tag in the selected candidate before renaming it. If +B could not find any tag information from MusicBrainz, you'll +have to fill out the candidate-tags-0 template. + +=head1 OPTIONS + +=over 4 + +=item B<-d> [B<--device>] I + +Use I as the CD-ROM device, instead of the default +"/dev/cdrom" or the environment variable CDDEV. + +=item B<-t> [B<--tracks>] I + +Archive only the first I tracks. This is handy for +ignoring data tracks. + +=back + +=head1 ENVIRONMENT + +=over 4 + +=item CDDEV + +B uses this to rip audio and save the cuesheet for a CD. It +makes some effort to check some common device names for FreeBSD, +Linux, and NetBSD by default. + +=back + +=head1 AUTHORS + +Written by Eric Gillespie . + +flac-archive is free software; you may redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +# Local variables: +# cperl-indent-level: 4 +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: + +# vi: set tabstop=4 expandtab: diff --git a/fa-tags b/fa-tags deleted file mode 100755 index 7d023ec..0000000 --- a/fa-tags +++ /dev/null @@ -1,66 +0,0 @@ -#! /usr/bin/env perl - -# $Id$ - -# See fa-flacd for documentation. - -use strict; -use warnings; - -use Env qw( - CDDEV -); - -use Getopt::Long; -use Pod::Usage; - -use MusicBrainz::Client::Simple; - -my $help; -GetOptions( - 'help|h|?' => \$help, - ) or pod2usage(); -$help and pod2usage(-exitstatus=>0, -verbose=>1); - -my $trackcount = shift or pod2usage(); - -my $mb; -if (defined($CDDEV)) { - $mb = new MusicBrainz::Client::Simple (device=>$CDDEV); -} else { - $mb = new MusicBrainz::Client::Simple; -} - -my @result = $mb->lookup_cd; -die($mb->get_error) unless $mb->success; - -open(F, '>candidate-tags-0') or die("open('>candidate-tags-0'): $!"); -print(F "$_=\n") for ('ARTIST', 'ALBUM', 'DATE'); -print(F "TITLE=\n") for 1 .. $trackcount; -close(F) or die("close('>candidate-tags-0'): $!"); - -my $i; -for my $album (@result) { - $i++; - open(F, '>', "candidate-tags-$i") or die("open('>candidate-tags-$i'): $!"); - - print(F 'ARTIST=', $album->get_artist->get_name, "\n"); - print(F 'ALBUM=', $album->get_name, "\n"); - - # MusicBrainz doesn't have dates yet; these are usually wrong anyway. - print(F "DATE=\n"); - - my @tracks = $album->get_tracks; - my $name; - my $track; - for my $j (1 .. $trackcount) { - if ($track = shift(@tracks)) { - $name = $track->get_name; - } else { - $name = ''; - } - print(F "TITLE=$name\n"); - } - - close(F) or die("close('>candidate-tags-$i'): $!"); -}