X-Git-Url: https://diplodocus.org/git/flac-archive/blobdiff_plain/a985282778e722a8f7753c277030bd7456f095fa..ed3b7d734a1cbea566f2d5cb9c4a9b3938e5a029:/fa-rip diff --git a/fa-rip b/fa-rip index 916c050..22140a5 100755 --- a/fa-rip +++ b/fa-rip @@ -23,68 +23,114 @@ use Env qw( 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 run_or_die { - my $command = shift; - my $status; - - $status = system($command); +sub mkcue { + my $device = shift; + my $trackcount = shift; - if (WIFEXITED($status)) { - if (($status = WEXITSTATUS($status)) != 0) { - die("$command exited with status $status"); + 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; + } } - } elsif (WIFSIGNALED($status)) { - die("$command killed with signal ", WTERMSIG($status)); - } elsif (WIFSTOPPED($status)) { - die("$command stopped with signal ", WSTOPSIG($status)); - } else { - die("Major horkage on system($command): \$? = $? \$! = $!"); + + 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; } -sub mkcue { - my $device = shift; +sub tags_file { + my $fn = shift; my $trackcount = shift; - my @command; - - push(@command, 'mkcue'); + my $various = shift; + my $artist = shift; + my $album = shift; + my $release_dates = shift; + my $fh; + my $i; + my $track; + my $name; - if (defined($trackcount)) { - push(@command, "-t $trackcount"); - } + 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($device)) { - push(@command, $device); + 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"); } - 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); + 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"); } - return $trackcount; + 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 @tracks; - my $name; - my $track; - my $j; + 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); @@ -97,43 +143,37 @@ sub tags { 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 = ''; + if ($various = $album->has_various_artists) { + if (not $seen_various) { + $seen_various = 1; + tags_file('candidate-tags-0v', $trackcount, 1); } - print(F "TITLE=$name\n"); } - close(F) or die("close(>candidate-tags-$i): $!"); + 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; + my @output; $device ||= '/dev/cdrom'; + if ($single_file) { + @output = ("1-$trackcount", 'wav'); + } else { + @output = ('-B'); + } - exec('cdparanoia', '-d', $device, "1-$trackcount", 'wav'); - # exec prints its own error message so just + exec('cdparanoia', '-d', $device, @output); die; } @@ -149,14 +189,18 @@ sub make_post_processor { } MAIN: { + my $no_mb; my $post_processor; - my $trackcount; + 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(); @@ -168,8 +212,8 @@ MAIN: { make_post_processor($post_processor); $trackcount = mkcue($CDDEV, $trackcount); - tags($CDDEV, $trackcount); - rip($CDDEV, $trackcount); + tags($CDDEV, $trackcount, $no_mb); + rip($CDDEV, $trackcount, $single_file); } @@ -178,9 +222,9 @@ __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. +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 @@ -217,9 +261,8 @@ ignoring data tracks. =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. +B uses this to rip audio and save the cuesheet for a CD. +MusicBrainz::Client can usually figure this out automatically. =back