]> diplodocus.org Git - flac-archive/commitdiff
Here's a simple hack to split single cue-containing flac files into
authorepg <>
Fri, 23 Jun 2006 01:30:43 +0000 (01:30 +0000)
committerepg <>
Fri, 23 Jun 2006 01:30:43 +0000 (01:30 +0000)
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...

flacsplit [new file with mode: 0755]

diff --git a/flacsplit b/flacsplit
new file mode 100755 (executable)
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 (<F>) {
+        /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);
+    }
+}