]> diplodocus.org Git - flac-archive/blob - flac2mp3
Need the g (global) flag on that substitution operation.
[flac-archive] / flac2mp3
1 #! /usr/bin/env perl
2
3 # $Id$
4
5 =head1 NAME
6
7 B<flac2mp3> - transcode FLAC file to MP3 files
8
9 =head1 SYNOPSIS
10
11 B<flac2mp3> I<file>
12
13 =head1 DESCRIPTION
14
15 B<flac2mp3> transcodes the FLAC file I<file> to MP3 files. I<file> is
16 the kind of FLAC file B<fa-flacd> generates. That is, it contains a
17 cue sheet, one TITLE tag per track listed therein, and ARTIST, ALBUM,
18 and DATE tags.
19
20 =cut
21
22 use strict;
23 use warnings;
24
25 use POSIX ':sys_wait_h';
26 use Pod::Usage;
27
28 sub run_or_die {
29 my $command = shift;
30 my $status;
31
32 $status = system($command);
33
34 if (WIFEXITED($status)) {
35 if (($status = WEXITSTATUS($status)) != 0) {
36 die("$command exited with status $status");
37 }
38 } elsif (WIFSIGNALED($status)) {
39 die("$command killed with signal ", WTERMSIG($status));
40 } elsif (WIFSTOPPED($status)) {
41 die("$command stopped with signal ", WSTOPSIG($status));
42 } else {
43 die("Major horkage on system($command): \$? = $? \$! = $!");
44 }
45 }
46
47 sub tformat {
48 return sprintf('%02d:%02d.%02d', @_);
49 }
50
51 sub get_decode_args {
52 my $fn = shift;
53 my @l;
54
55 open(F, '-|', 'metaflac', '--export-cuesheet-to=-', $fn);
56 while (<F>) {
57 /INDEX 01 (\d\d):(\d\d):(\d\d)$/ or next;
58 push(@l, [$1, $2, $3]);
59 }
60
61 my @args;
62 for my $i (0..$#l) {
63 my $arg = ["--skip=" . tformat(@{$l[$i]})];
64 my $next = $l[$i+1];
65 if (defined($next)) {
66 if ($next->[2] == 0) {
67 if ($next->[1] == 0) {
68 push(@$arg, '--until=' . tformat($next->[0] - 1, 59, 74));
69 } else {
70 push(@$arg, '--until=' . tformat($next->[0], $next->[1] - 1,
71 74));
72 }
73 } else {
74 push(@$arg, '--until=' . tformat($next->[0], $next->[1],
75 $next->[2] - 1));
76 }
77 }
78 push(@args, $arg);
79 }
80
81 return @args;
82 }
83
84 # Return the ARTIST, ALBUM, and DATE tags followed by the TITLE tags
85 # in the file FN.
86 sub get_tags {
87 my $fn = shift;
88 my $tag;
89 my $value;
90 my $artist;
91 my $album;
92 my $date;
93 my @titles;
94
95 open(TAGS, '-|', 'metaflac', '--export-vc-to=-', $fn)
96 or die("open(metaflac --export-vc-to=- $fn): $!");
97 while (<TAGS>) {
98 chomp;
99
100 ($tag, $value) = split(/=/, $_, 2);
101
102 if (/^ARTIST=/) {
103 $artist = $value;
104 } elsif (/^ALBUM=/) {
105 $album = $value;
106 } elsif (/^DATE=/) {
107 $date = $value;
108 } elsif (/TITLE/) {
109 push(@titles, $value);
110 }
111 }
112 close(TAGS) or die("close(metaflac --export-vc-to=- $fn): $?");
113
114 return ($artist, $album, $date, @titles);
115 }
116
117 sub flac2mp3 {
118 my $fn = shift;
119 my $title = shift;
120 my $artist = shift;
121 my $album = shift;
122 my $date = shift;
123 my $track = shift;
124 my $skip_arg = shift;
125 my $until_arg = shift;
126 my $outfile;
127
128 # We'll be putting these in single quotes, so we need to escape
129 # any single quotes in the filename by closing the quote ('),
130 # putting an escaped quote (\'), and then reopening the quote (').
131 for ($fn, $title, $artist, $album, $date) {
132 s/'/'\\''/g;
133 }
134
135 $outfile = sprintf("$artist ($album) \%02s $title.mp3", $track);
136 $outfile =~ s/\//_/g;
137
138 $until_arg ||= '';
139 run_or_die(join(' ', "flac -cd $skip_arg $until_arg '$fn'",
140 " | lame --preset standard --tt '$title' --ta '$artist'",
141 " --tl '$album' --ty '$date' --tn $track - '$outfile'"));
142 }
143
144 MAIN: {
145 my $fn = shift or pod2usage();
146 my @args = get_decode_args($fn);
147 my ($artist, $album, $date, @titles) = get_tags($fn);
148
149 for my $i (0..$#titles) {
150 flac2mp3($fn, $titles[$i], $artist, $album, $date, $i + 1,
151 @{$args[$i]});
152 }
153 }
154
155 \f
156 __END__
157
158 =head1 AUTHORS
159
160 Written by Eric Gillespie <epg@pretzelnet.org>.
161
162 =cut
163
164 # Local variables:
165 # cperl-indent-level: 4
166 # perl-indent-level: 4
167 # indent-tabs-mode: nil
168 # End:
169
170 # vi: set tabstop=4 expandtab: