X-Git-Url: https://diplodocus.org/git/flac-archive/blobdiff_plain/c9e06a2c36a0f00114410fa5db28073b3f0fa439..2d9012033ed415b9d851649ec7e206072927f5a3:/fa-rip diff --git a/fa-rip b/fa-rip index 1898dd5..20f4190 100755 --- a/fa-rip +++ b/fa-rip @@ -1,37 +1,280 @@ -#! /usr/bin/env zsh +#! /usr/bin/env perl # $Id$ +# $URL$ -set -e -setopt NULL_GLOB +=head1 NAME -get_cddev () { - local raw - raw=$(sysctl -n kern.rawpartition > /dev/null | awk '{printf "%c",97+$0}') +B - rip a CD for B - for CDDEV in /dev/{cdroms/cdrom*,cdrom*,rcd*${raw},{a,}cd*c}; do - [[ -e ${CDDEV} ]] && return 0 - done +=head1 SYNOPSIS - return 1 +B [B<-d> I] [B<-p> I [B<-t> I] + +=cut + +use strict; +use warnings; + +use Env qw( + CDDEV +); + +use Fcntl qw(O_CREAT O_WRONLY); +use File::Temp; +use Getopt::Long qw(:config gnu_getopt no_ignore_case); +use List::Util qw(min); +use POSIX ':sys_wait_h'; +use Pod::Usage; + +use MusicBrainz::Client; +use MusicBrainz::Queries qw( + MBQ_GetCDTOC + MBE_TOCGetFirstTrack + MBE_TOCGetLastTrack + MBE_TOCGetTrackSectorOffset +); +use MusicBrainz::Client::Simple; + +sub mkcue { + my $device = shift; + my $trackcount = shift; + + my $mb = MusicBrainz::Client->new; + defined($device) and $mb->set_device($device); + + $mb->query(MBQ_GetCDTOC) or die($mb->get_query_error); + + open(my $fh, '>cue') or die("open('>cue'): $!"); + print($fh "FILE \"dummy.wav\" WAVE\n"); + print($fh " TRACK 01 AUDIO\n"); + print($fh " INDEX 01 00:00:00\n"); + + my $first = $mb->get_result_data(MBE_TOCGetFirstTrack) + 1; + $trackcount = min($trackcount, $mb->get_result_data(MBE_TOCGetLastTrack)); + # There is frequently (always?) an offset of 150 sectors, so + # we'll subtract this offset from each track offset. + my $something = $mb->get_result_data1(MBE_TOCGetTrackSectorOffset, 2); + + for my $track ($first .. $trackcount) { + my $off = $mb->get_result_data1(MBE_TOCGetTrackSectorOffset, $track+1); + $off -= $something; + + my ($minutes,$seconds)=(0,0); + my $sectors = $off % 75; + if ($off >= 75) { + $seconds = $off / 75; + if ($seconds >= 60) { + $minutes = $seconds / 60; + $seconds = $seconds % 60; + } + } + + printf($fh " TRACK %02d AUDIO\n", $track); + printf($fh " INDEX 01 %02d:%02d:%02d\n", + $minutes, $seconds, $sectors); + } + + close($fh) or die("close(>cue): $!"); + + return $trackcount; } -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 +sub tags_file { + my $fn = shift; + my $trackcount = shift; + my $various = shift; + my $artist = shift; + my $album = shift; + my $release_dates = shift; + my $fh; + my $i; + my $track; + my $name; + + open($fh, '>', $fn) or die("open('>$fn'): $!"); + print($fh 'ARTIST=', (defined($artist) and $artist or ''), "\n"); + print($fh 'ALBUM=', (defined($album) and $album or ''), "\n"); + + if (defined($release_dates) and %$release_dates) { + while (my ($country, $date) = each(%$release_dates)) { + print($fh "DATE[$country]=$date\n"); + } + } else { + print($fh "DATE=\n"); + } + + for $i (1 .. $trackcount) { + $various and print($fh "ARTIST[$i]=\n"); + if ($track = shift(@_)) { + $name = $track->get_name; + } else { + $name = ''; + } + print($fh "TITLE[$i]=$name\n"); + } + + close($fh) or die("close(>$fn): $!"); +} + +sub tags { + my $device = shift; + my $trackcount = shift; + my $no_mb = shift; + my $mb; + my @results; + my $album; + my $i; + my $various; + my $seen_various; + + tags_file('candidate-tags-0', $trackcount, 0); + + defined($no_mb) and $no_mb and return; + + 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); + } + + for $album (@results) { + $i++; + + if ($various = $album->has_various_artists) { + if (not $seen_various) { + $seen_various = 1; + tags_file('candidate-tags-0v', $trackcount, 1); + } + } + + my %dates = $album->get_release_dates; + tags_file("candidate-tags-$i", $trackcount, $various, + $album->get_artist->get_name, $album->get_name, + \%dates, $album->get_tracks); + } +} + +sub rip { + my $device = shift; + my $trackcount = shift; + my $single_file = shift; + + $device ||= '/dev/cdrom'; + + exec('cdparanoia', '-d', $device, + (($single_file and ("1-$trackcount", 'wav')) or ('-B'))); + # exec prints its own error message so just + die; +} + +sub make_post_processor { + my $command = shift; + + defined($command) or return; + + sysopen(F, 'post-processor', O_CREAT | O_WRONLY, 0555) + or die("sysopen(post-processor, O_CREAT | O_WRONLY, 0555): $!"); + print(F $command, ' "$@"', "\n"); + close(F) or die("close(post-processor, O_CREAT | O_WRONLY, 0555): $!"); +} + +MAIN: { + my $no_mb; + my $post_processor; + my $single_file; + my $trackcount = 99; + my $help; + my $tempdir; + + GetOptions( + 'device|d=s' => \$CDDEV, + 'no-musicbrainz|m' => \$no_mb, + 'post-processor|p=s', \$post_processor, + 'single-file|s' => \$single_file, + '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): $!"); + + make_post_processor($post_processor); + $trackcount = mkcue($CDDEV, $trackcount); + tags($CDDEV, $trackcount, $no_mb); + rip($CDDEV, $trackcount, $single_file); +} + + +__END__ + +=head1 DESCRIPTION + +B creates a temporary directory for storage of its +intermediate files, uses MusicBrainz to create the "cue" file and +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<-p> [B<--post-processor>] I + +Create a "post-processor" file in the temporary directory containing +the line 'I "$@"'. See B's man page for +information about this hook. + +=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. +MusicBrainz::Client can usually figure this out automatically. + +=back + +=head1 AUTHORS -dir=$(mktemp -d flac-archive.XXXXXXXXXX) -cd ${dir} +Written by Eric Gillespie . -cdrdao read-toc --device ${CDDEV} --driver generic-mmc toc -toc2cue toc cue +flac-archive is free software; you may redistribute it and/or modify +it under the same terms as Perl itself. -trackcount=$(grep -c 'TRACK.*AUDIO' cue) +=cut -fa-tags ${trackcount} +# Local variables: +# cperl-indent-level: 4 +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: -exec cdparanoia -d ${CDDEV} 1-$(grep -c 'TRACK.*AUDIO' cue) wav +# vi: set tabstop=4 expandtab: