source: flacrip/trunk/flactrack @ 48

Last change on this file since 48 was 48, checked in by peter, 8 years ago

Default the --directory to the current directory.

Then, if the --all flag is set, add the ARTIST.DATE.ALBUM directory to the
end of the base directory given by --directory.

  • Property svn:executable set to *
File size: 4.8 KB
Line 
1#!/usr/bin/perl -w
2use strict;
3
4# extract one or more tracks from a FLAC file using its embedded cuesheet
5# save those tracks as wav or mp3 files
6# can also run them through a sox filter
7# TODO: separate sox filter for each track!
8
9use FindBin;
10use lib "$FindBin::RealBin/lib";
11
12use Getopt::Long qw{:config no_ignore_case};
13use File::Spec::Functions qw{catfile splitpath};
14use File::Path;
15use Audio::FLAC::Header;
16use MusicBrainz;
17use Text::Unidecode;
18use Cwd;
19
20# default to using tags
21my $TAGS = 1;
22
23GetOptions(
24    'D=s' => \my %TRACKS,
25    't=s' => \my $TYPE,
26    'x=s' => \my $SOX_FILTER,
27    'all|a' => \my $ALL,
28    'dir|d=s' => \my $DIRECTORY,
29    'ascii-tags' => \my $ASCII_TAGS,
30    'tags!' => \$TAGS,
31    'force' => \my $FORCE,
32);
33
34my $FLAC_FILE = shift or die "Need a flac file to decode";
35
36# default to mp3
37$TYPE ||= 'mp3';
38
39# default to the current directory
40$DIRECTORY ||= cwd;
41
42my $flac = Audio::FLAC::Header->new($FLAC_FILE) or die "Can't read FLAC header from $FLAC_FILE\n";
43
44# for getting track metadata from MusicBrainz
45my $info;
46if ($ALL || ($TYPE eq 'mp3' && $TAGS)) {
47    (my $properties_file = $FLAC_FILE) =~ s/\.flac$/.properties/;
48    if (-e $properties_file) {
49        require Config::Properties;
50
51        # the properties are in UTF-8; mark them as such so unidecode works correctly later
52        my $properties = Config::Properties->new(file => $properties_file, encoding => 'utf8');
53        $info = $properties->getProperties;
54    } else {
55
56        my $discid = $flac->tags('MUSICBRAINZ_DISCID') or warn "No MUSICBRAINZ_DISCID tag in $FLAC_FILE\n" if $flac;
57        #TODO: calculate TOC and DISCID from cuesheet if there is no MUSICBRAINZ_DISCID tag present
58
59        $info = get_musicbrainz_info($discid);
60    }
61    exit unless $info;
62}
63
64if ($ALL) {
65    # if we are converting an entire album file, create a directory to hold the mp3s
66    # name the directory ARTIST.DATE.ALBUM, so it will sort nicely
67    my $base_dir = $DIRECTORY;
68    my $album_dir = join('.', map { to_filename($_) } @{$info}{qw{ALBUMARTISTSORT ORIGINALDATE ALBUM}});
69    # need to append "disc#" if this is a multidisc album
70    if ($info->{DISCTOTAL} > 1) {
71        $album_dir .= '.disc_' . $info->{DISCNUMBER};
72    }
73    $DIRECTORY = catfile($base_dir, $album_dir);
74    if (-e $DIRECTORY and not $FORCE) {
75        die "$DIRECTORY already exists, skipping. (Use --force to overwrite)\n";
76    }
77    #die $DIRECTORY;
78}
79
80if ($ALL) {
81    die "Use of --all requires a --directory\n" unless $DIRECTORY;
82    die "No track info found on MusicBrainz for $FLAC_FILE\n" unless $info;
83    use YAML;
84    my $cuesheet = $flac->cuesheet;
85    my $count = scalar grep { /TRACK \d\d/ } @{ $flac->cuesheet };
86    print "Found $count tracks\n";
87    #TODO: default to just 01, 02, etc. if there is no $info
88    %TRACKS = map {
89        $_ => catfile($DIRECTORY, sprintf('%02d.%s', $_, to_filename($info->{sprintf 'TRACK%02d.TITLE', $_})))
90    } (1 .. $count);
91    #print Dump(\%TRACKS);
92    for my $tracknum (sort { $a <=> $b } keys %TRACKS) {
93        printf "%2d: %s\n", $tracknum, $TRACKS{$tracknum};
94    }
95    mkpath($DIRECTORY);
96}
97
98#TODO: all the option of sorting by tracknum or not
99#while (my ($tracknum, $title) = each %TRACKS) {
100for my $tracknum (sort { $a <=> $b } keys %TRACKS) {
101    if ($tracknum !~ /^\d+$/) {
102        warn "Don't know what to do with track number '$tracknum'";
103        next;
104    }
105    my $start = $tracknum . '.1';
106    my $end = $tracknum + 1 . '.1';
107    my $cmd = qq{flac -d --cue $start-$end $FLAC_FILE -o - };
108
109    if ($SOX_FILTER) {
110        $cmd .= qq{| sox -t wav - -t wav - $SOX_FILTER };
111    }
112
113    my $title = quotemeta($TRACKS{$tracknum});
114    if ($TYPE eq 'mp3') {
115        # bitrate of 192
116        $cmd .= qq{| lame -b 192};
117        # if there is track info, add it as ID3 tags
118        if ($info) {
119            my $track_key = sprintf 'TRACK%02d', $tracknum;
120            $cmd .= sprintf q{ --tt %s --ta %s --tl %s --ty %d --tn %d},
121                quote($ASCII_TAGS ? unidecode($info->{"$track_key.TITLE"}) : $info->{"$track_key.TITLE"}),
122                quote($ASCII_TAGS ? unidecode($info->{"$track_key.ARTIST"}) : $info->{"$track_key.ARTIST"}),
123                quote($ASCII_TAGS ? unidecode($info->{ALBUM}) : $info->{ALBUM}),
124                ($info->{ORIGINALDATE} =~ /^(\d\d\d\d)/)[0],
125                $tracknum;
126        }
127        $cmd .= qq{ - $title.mp3};
128    } elsif ($TYPE eq 'wav') {
129        $cmd .= qq{> $title.wav};
130    } else {
131        die "Unknown type: $TYPE\n";
132    }
133    #die $cmd;
134    system $cmd;
135    die "\nFLAC decoding canceled\n" if ($? & 127);
136
137    print "\n" if $SOX_FILTER;
138}
139
140sub quote {
141    my ($string) = @_;
142    $string =~ s/"/\\"/g;
143    return qq{"$string"};
144}
145
146sub to_filename {
147    my @strings = @_;
148    return map {
149        s/&/ and /g;
150        unidecode($_);
151        s/[^a-z0-9-_ ]+//gi;
152        s/ +/_/g;
153        lc;
154    } @strings;
155}
Note: See TracBrowser for help on using the repository browser.