source: flacrip/trunk/flactrack @ 13

Last change on this file since 13 was 13, checked in by peter, 10 years ago
  • if there is a properties file with the same base name as the flac file, use it as the source of metadata info instead of doing a lookup to the MusicBrainz web service
  • don't rely on the TRACKS array key of $info to be present, since it is not there when reading from a properties file
  • Property svn:executable set to *
File size: 4.2 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;
17
18GetOptions(
19    'D=s' => \my %TRACKS,
20    't=s' => \my $TYPE,
21    'x=s' => \my $SOX_FILTER,
22    'all|a' => \my $ALL,
23    'dir|d=s' => \my $DIRECTORY,
24);
25
26my $FLAC_FILE = shift or die "Need a flac file to decode";
27
28# default to mp3
29$TYPE ||= 'mp3';
30
31# default the directory to be named like the flac file
32($DIRECTORY ||= $FLAC_FILE) =~ s/\.flac$//;
33
34my $flac = Audio::FLAC::Header->new($FLAC_FILE) or die "Can't read FLAC header from $FLAC_FILE\n";
35
36# for getting track metadata from MusicBrainz
37my $info;
38if ($ALL || $TYPE eq 'mp3') {
39    (my $properties_file = $FLAC_FILE) =~ s/\.flac$/.properties/;
40    if (-e $properties_file) {
41        require Config::Properties;
42
43        my $properties = Config::Properties->new(file => $properties_file);
44        $info = $properties->getProperties;
45    } else {
46
47        my $discid = $flac->tags('MBZ_DISCID') or warn "No MBZ_DISCID tag in $FLAC_FILE\n" if $flac;
48        #TODO: calculate TOC and DISCID from cuesheet if there is no MBZ_DISCID tag present
49
50        $info = get_musicbrainz_info($discid);
51    }
52    exit unless $info;
53
54    # if we have metadata, change the directory name to ARTIST.DATE.ALBUM, so it will sort nicely
55    my $base_dir = (splitpath($DIRECTORY))[1];
56    my $album_dir = join('.', map { to_filename($_) } @{$info}{qw{ALBUMARTISTSORT ORIGINALDATE ALBUM}});
57    # need to append "disc#" if this is a multidisc album
58    if ($info->{DISCTOTAL} > 1) {
59        $album_dir .= '.disc_' . $info->{DISCNUMBER};
60    }
61    $DIRECTORY = catfile($base_dir, $album_dir);
62    #die $DIRECTORY;
63}
64
65if ($ALL) {
66    die "Use of --all requires a --directory\n" unless $DIRECTORY;
67    die "No track info found on MusicBrainz for $FLAC_FILE\n" unless $info;
68    use YAML;
69    my $cuesheet = $flac->cuesheet;
70    my $count = scalar grep { /TRACK \d\d/ } @{ $flac->cuesheet };
71    print "Found $count tracks\n";
72    #TODO: default to just 01, 02, etc. if there is no $info
73    %TRACKS = map {
74        $_ => catfile($DIRECTORY, sprintf('%02d.%s', $_, to_filename($info->{sprintf 'TRACK%02d.TITLE', $_})))
75    } (1 .. $count);
76    #print Dump(\%TRACKS);
77    for my $tracknum (sort { $a <=> $b } keys %TRACKS) {
78        printf "%2d: %s\n", $tracknum, $TRACKS{$tracknum};
79    }
80    mkpath($DIRECTORY);
81}
82
83#TODO: all the option of sorting by tracknum or not
84#while (my ($tracknum, $title) = each %TRACKS) {
85for my $tracknum (sort { $a <=> $b } keys %TRACKS) {
86    if ($tracknum !~ /^\d+$/) {
87        warn "Don't know what to do with track number '$tracknum'";
88        next;
89    }
90    my $start = $tracknum . '.1';
91    my $end = $tracknum + 1 . '.1';
92    my $cmd = qq{flac -d --cue $start-$end $FLAC_FILE -o - };
93
94    if ($SOX_FILTER) {
95        $cmd .= qq{| sox -t wav - -t wav - $SOX_FILTER };
96    }
97
98    my $title = quotemeta($TRACKS{$tracknum});
99    if ($TYPE eq 'mp3') {
100        # bitrate of 192
101        $cmd .= qq{| lame -b 192};
102        # if there is track info, add it as ID3 tags
103        if ($info) {
104            my $track_key = sprintf 'TRACK%02d', $tracknum;
105            $cmd .= sprintf q{ --tt %s --ta %s --tl %s --tn %d},
106                quote($info->{"$track_key.TITLE"}),
107                quote($info->{"$track_key.ARTIST"}),
108                quote($info->{ALBUM}),
109                $tracknum;
110        }
111        $cmd .= qq{ - $title.mp3};
112    } elsif ($TYPE eq 'wav') {
113        $cmd .= qq{> $title.wav};
114    } else {
115        die "Unknown type: $TYPE\n";
116    }
117    #die $cmd;
118    system $cmd;
119    die "\nFLAC decoding canceled\n" if ($? & 127);
120
121    print "\n" if $SOX_FILTER;
122}
123
124sub quote {
125    my ($string) = @_;
126    $string =~ s/"/\\"/g;
127    return qq{"$string"};
128}
129
130sub to_filename {
131    my @strings = @_;
132    #TODO: deal with non-ascii characters (unidecode) e.g. ü --> ue
133    return map {
134        s/&/ and /g;
135        s/[^a-z0-9-_ ]+//gi;
136        s/ +/_/g;
137        lc;
138    } @strings;
139}
Note: See TracBrowser for help on using the repository browser.