From: epg <> Date: Fri, 23 Jun 2006 01:30:43 +0000 (+0000) Subject: Here's a simple hack to split single cue-containing flac files into X-Git-Url: https://diplodocus.org/git/flac-archive/commitdiff_plain/cbfbda845d7b4c16e242dc7ce2c682824e5a3151?hp=e779b6bf6a16f03fb177fbd47686113077163627 Here's a simple hack to split single cue-containing flac files into single-track flac files. It's almost entirely a copy/paste of parts of fa-flacd and flac2mp3; all the common parts really should be factored out... --- diff --git a/flacsplit b/flacsplit new file mode 100755 index 0000000..b598985 --- /dev/null +++ b/flacsplit @@ -0,0 +1,220 @@ +#! /usr/bin/env perl + +use strict; +use warnings; + +use Getopt::Long qw(:config gnu_getopt no_ignore_case); +use POSIX ':sys_wait_h'; +use Pod::Usage; + +my $quiet; +my $verbose; + +sub verbose { + $verbose and print(STDERR $_) for @_; +} + +sub run_or_die { + my $command = shift; + my $status; + + $verbose and print(STDERR "$command\n"); + $status = system($command); + + if (WIFEXITED($status)) { + if (($status = WEXITSTATUS($status)) != 0) { + die("$command exited with status $status"); + } + } 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): \$? = $? \$! = $!"); + } +} + +sub tformat { + return sprintf('%02d:%02d.%02d', @_); +} + +sub get_decode_args { + my $fn = shift; + my @l; + + open(F, '-|', 'metaflac', '--export-cuesheet-to=-', $fn); + while () { + /INDEX 01 (\d\d):(\d\d):(\d\d)$/ or next; + push(@l, [$1, $2, $3]); + } + + my @args; + for my $i (0..$#l) { + my $arg = ["--skip=" . tformat(@{$l[$i]})]; + my $next = $l[$i+1]; + if (defined($next)) { + if ($next->[2] == 0) { + if ($next->[1] == 0) { + push(@$arg, '--until=' . tformat($next->[0] - 1, 59, 74)); + } else { + push(@$arg, '--until=' . tformat($next->[0], $next->[1] - 1, + 74)); + } + } else { + push(@$arg, '--until=' . tformat($next->[0], $next->[1], + $next->[2] - 1)); + } + } + push(@args, $arg); + } + + if (@args == 0) { + die('no cue sheet'); + } + + return @args; +} + +# Return the ARTIST, ALBUM, and DISCNUMBER followed by a list of all +# the lines in the file FN. +sub get_tags { + my $fp = shift; + my $fn = shift; + my $tag; + my $value; + my $artist; + my $album; + my $discnum; + my @tags; + + while (<$fp>) { + chomp; + push(@tags, $_); + + ($tag, $value) = split(/=/, $_, 2); + + if (/^ARTIST=/i) { + $artist = $value; + verbose("ARTIST $artist from $fn\n"); + } elsif (/^ALBUM=/i) { + $album = $value; + verbose("ALBUM $album from $fn\n"); # cperl-mode sucks " + } elsif (/^DISCNUMBER=/i) { + $discnum = int($value); + verbose("DISCNUMBER $discnum from $fn\n"); + } + } + + return ($artist, $album, $discnum, @tags); +} + +sub track_tags { + my $h = shift; + my @result; + + while (my ($key, $vall) = each(%$h)) { + for my $val (@$vall) { + push(@result, "$key=$val") + } + } + + return @result; +} + +sub flacsplit { + my $fn = shift; + my $artist; + my $album; + my $discnum; + my @tags; + my $outdir; + my $outfile; + + open(my $fp, '-|', 'metaflac', '--export-vc-to=-', $fn) + or die("open(metaflac --export-vc-to=- $fn): $!"); + ($artist, $album, $discnum, @tags) = get_tags($fp, $fn); + close($fp) or die("close(metaflac --export-vc-to=- $fn): $?"); + for ($artist, $album) { + s/'/'\\''/g; + s|/|_|g; + } + + -d $artist or mkdir($artist) or die("mkdir($artist): $!"); + $outdir = "$artist/$album"; + -d "$outdir" or mkdir("$outdir") or die("mkdir($outdir): $!"); + + # Go over @tags, store all [n] tags in a list keyed by n in + # %tracks_to_tags, store all ARTIST (not ARTIST[n]) tags in + # @disc_artist, and leave the rest in @tags. + my %tracks_to_tags; + my @disc_artist; + my @tmp; + for my $tag (@tags) { + if ($tag =~ /^([^[]+)\[(\d+)]=(.*)/) { + push(@{$tracks_to_tags{$2}->{$1}}, $3); + } elsif ($tag =~ /^ARTIST=/) { + push(@disc_artist, $tag); + } else { + push(@tmp, $tag); + } + } + @tags = @tmp; + + $fn =~ s/'/'\\''/g; + + for my $tracknum (sort(map(int, keys(%tracks_to_tags)))) { + my $title = join(' ', map(split, @{$tracks_to_tags{$tracknum}->{'TITLE'}})); + $title =~ s/'/'\\''/g; + $title =~ s|/|_|g; + $outfile = join('/', + $outdir, + join(' ', + (defined($discnum) + ? sprintf('%02d', $discnum) + : ()), + sprintf('%02d', $tracknum), + $title)); + + # If we have ARTIST[n] tags for this track, set @track_artist + # to the empty list; they will go in along with the other [n] + # tags. + my @track_artist; + if (exists($tracks_to_tags{$tracknum}->{'ARTIST'})) { + @track_artist = (); + } else { + @track_artist = @disc_artist; + } + + my $flac_options = ''; + my ($skip_arg, $until_arg) = @{$_[$tracknum - 1]}; + $skip_arg ||= ''; + $until_arg ||= ''; + run_or_die(join(' ', + "flac $flac_options -cd $skip_arg $until_arg '$fn'", + " | flac -o '$outfile.flac' -V --no-padding --best", + map({ s/'/'\\''/g; ('-T', "'$_'") } + @track_artist, + @tags, + "TRACKNUMBER=$tracknum", + track_tags($tracks_to_tags{$tracknum})), + '-')); + } + + return 0; +} + +MAIN: { + my $help; + GetOptions( + 'quiet|q' => \$quiet, + 'verbose|v' => \$verbose, + 'help|h|?' => \$help, + ) or pod2usage(); + $help and pod2usage(-exitstatus=>0, -verbose=>1); + + @ARGV or pod2usage(); + for my $fn (@ARGV) { + my @args = get_decode_args($fn); + flacsplit($fn, @args); + } +}