=cut
-# POSIX.1-2017, Base Definitions, 3.282 Portable Filename Character Set says
-# [A-Za-z0-9._-]
-# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282
-
-# Flac tags (set with `flac -T fieldname=...`) are Vorbis comments.
-# https://www.xiph.org/vorbis/doc/v-comment.html#fieldnames field names we care about:
-# - TITLE - Track/Work name
-# - VERSION - may be used to differentiate multiple versions of the same track title in a single collection. (e.g. remix info)
-# - ALBUM - The collection name to which this track belongs
-# - TRACKNUMBER - The track number of this piece
-# - ARTIST - The artist generally considered responsible for the track
-# - DATE - Date the track was recorded (XXX I use US release date)
-# I use one more, though I'm unsure where I got it. Did I make it up?
-# - DISCNUMBER - number in multi-disc collection
-
-# Input is a directory containing:
-# - trackNN.cdda.wav - WAV format files ripped from CD-DA audio tracks where NN is track number 01 - 99
-# - tags - described below
-# - cover.front.jpeg - optional JPEG format file containing the album front cover
-
-# The tags file is composed of two blocks:
-# 1. album tags
-# 2. track tags
-# Album tags may be:
-# - ALBUM
-# - ARTIST
-# - DATE
-# - DISCNUMBER (optional)
-
-# Track tags may be any of the rest of the tags listed above, suffixed with
-# [N] where N is the track number. ARTIST, and only ARTIST, may also appear
-# in the track tags. In this case, it overrides the album artist. In order
-# to add artists, the album artist must be listed again. For example, Reba
-# McEntire's "The Heart Won't Lie" on the album "It's Your Call" features
-# Vince Gill, and is specified as:
-# ARTIST=Reba McEntire
-# ALBUM=It's Your Call
-# TITLE[5]=The Heart Won't Lie
-# ARTIST[5]=Reba McEntire
-# ARTIST[5]=Vince Gill
-
package epg::flac::archive::encode;
use v5.12;
use warnings;
-sub mangle_for_file_name {
- my $fn = shift;
- $fn =~ s/\s+/_/g;
- $fn =~ s/[^A-Za-z0-9._]/-/g;
- $fn
-}
-
-sub read_tags {
- my $fn = shift;
- open(my $fh, '<', $fn) || die("open($fn): $!");
- my %album;
- my @tracks;
- my $tracknum = 0;
- while (<$fh>) {
- chomp;
- s/^([^=[\]]+)(\[(\d+)])?=// || die("no field name in $_");
- my $name = $1;
- # TODO validate $name
- # TODO album tags should be illegal after track tags
- if (defined($3)) {
- if ($3 == $tracknum + 2) {
- $tracknum++;
- } elsif ($3 != $tracknum + 1) {
- $tracknum++; # increment from 0 to 1 for error message
- die("illegal track number jump from $tracknum to $3")
- }
- push(@{$tracks[$tracknum]->{$name}}, $_);
- } else {
- push(@{$album{$name}}, $_);
- }
- }
-
- \%album, \@tracks
-}
-
-sub quote {
- my $s = shift;
- $s =~ s/'/'\\''/g;
- "'$s'"
+use FindBin;
+use Pod::Usage;
+
+use lib $FindBin::Bin;
+
+require 'tags.pl';
+epg::flac::archive::tags->import(
+ qw[
+ read_tags
+ mangle_for_file_name
+ quote
+ track_tags_from_disc
+ two_digits
+ ]);
+
+sub filename {
+ my ($tracknumber, $tags) = @_;
+ my $title = $tags->{TITLE} // die('title required');
+ my @discnumber = @{$tags->{DISCNUMBER} // []};
+ my @partnumber = @{$tags->{PARTNUMBER} // []};
+ my @version = @{$tags->{VERSION} // []};
+ join('-',
+ (map { two_digits($_) } @discnumber),
+ two_digits($tracknumber),
+ mangle_for_file_name(join('TODO multi-title?', @$title)),
+ # TODO more than one VERSION?
+ (map { mangle_for_file_name($_) } @version),
+ @partnumber,
+ )
+ . '.flac',
}
sub main {
- my $input_directory = shift;
- my ($album, $tracks) = read_tags("$input_directory/tags");
+ my $input_directory = shift || pod2usage();
+ my $fn = "$input_directory/tags";
+ open(my $fh, '<', $fn) || die("open($fn): $!");
+ my ($album, $tracks) = read_tags($fh);
if (!defined($album->{ALBUM}) || scalar(@{$album->{ALBUM}}) != 1) {
die('exactly one ALBUM tag required')
}
if (scalar(@{$album->{DISCNUMBER}}) != 1) {
die('one or zero DISCNUMBER tags required')
}
- @discnumber = (sprintf('%02d', $album->{DISCNUMBER}->[0]));
+ @discnumber = ($album->{DISCNUMBER}->[0]);
}
my $dir = join('/', '..', mangle_for_file_name($artist), mangle_for_file_name($album_tag));
say('set -ex');
+ say('cd ', quote($input_directory));
say('mkdir -p ', quote($dir));
my $tracknum = 0;
for my $track (@$tracks) {
$track->{DISCNUMBER} = \@discnumber;
}
my $title = join(' ', @{$track->{TITLE}});
- my $tracknum_s = sprintf('%02d', $tracknum);
my $fn = join('/',
$dir,
- join('_',
- @discnumber,
- $tracknum_s,
- mangle_for_file_name($title) . '.flac',
+ filename(
+ $tracknum,
+ {track_tags_from_disc($album, $tracks, $tracknum)}
),
);
-e $fn && die("cowardly refusing to clobber $fn");
- my @pictures = ('--picture', quote('3|image/jpeg|||cover.front')); # TODO optional
+ my @pictures = ('--picture', quote('3|image/jpeg|||cover.front.jpeg')); # TODO optional
+ @pictures = (); # XXX
+ my $tracknum_s = two_digits($tracknum);
say(join(' ',
'flac -o',
quote($fn),
"track$tracknum_s.cdda.wav"
));
}
- say('rm tags cover.front');
+ say('rm tags cover.front.jpeg');
return 0;
}
}
1;
-