source: flacrip/trunk/lib/MusicBrainz.pm @ 43

Last change on this file since 43 was 43, checked in by peter, 9 years ago

fallback to an interactive selection of the release if there are multiple hits with the same barcode and disc ID

File size: 5.5 KB
Line 
1package MusicBrainz;
2
3use strict;
4use warnings;
5
6our @ISA    = qw{Exporter};
7our @EXPORT = qw{get_musicbrainz_info lookup_release};
8
9#use WebService::MusicBrainz;
10use LWP;
11use XML::XPath;
12use XML::XPath::XMLParser;
13
14sub lookup_release {
15    my ($discid) = @_;
16    my $ua = LWP::UserAgent->new;
17
18    #my $uri = URI->new('http://musicbrainz.org/ws/1/release/');
19    #$uri->query_form(type => 'xml', discid => $discid);
20    my $uri = URI->new("http://musicbrainz.org/ws/2/discid/$discid");
21    $uri->query_form(inc => 'artists+labels+recordings+release-groups+artist-credits');
22
23    my $res = $ua->get($uri);
24    # pause for a second, so we don't run afoul of the MusicBrainz API TOS
25    sleep 1;
26
27    warn $res->status_line, "\n" if $res->code != 200;
28    return if $res->code >= 400;
29    #TODO: if we get a 5xx error, retry?
30
31    return $res->decoded_content;
32}
33
34sub interactive_select_release {
35    my ($xpath, $release_count, $discid, @releases) = @_;
36    my $base = 'http://musicbrainz.org/release/';
37
38    my $i = 1;
39    # present the user with an interactive menu to pick/confirm the correct release ID
40    warn "$release_count release(s) found matching $discid\n";
41    for my $release (@releases) {
42        warn sprintf "%2d) $base%s %s %s (%s)\n", 
43        $i++,
44        $xpath->findvalue('@id', $release)->value,
45        $xpath->findvalue('.//label-info/label/name', $release)->value,
46        $xpath->findvalue('.//label-info/catalog-number', $release)->value,
47        $xpath->findvalue('barcode', $release)->value;
48    }
49
50    my $selection = 0;
51
52    while ($selection < 1 || $selection > $release_count) {
53        print STDERR "Select a release (1-$release_count): ";
54        $selection = <STDIN>;
55        chomp $selection;
56        return if $selection =~ /^q/i;
57    }
58
59    return $releases[$selection - 1];
60}
61
62sub select_release {
63    my ($xpath, $params) = @_;
64
65    my $discid = $params->{discid};
66    my $barcode = $params->{barcode};
67
68    if ($barcode) {
69        # try to pick the release automatically by the barcode
70        my @releases = $xpath->findnodes("//release[barcode='$barcode']");
71        if (@releases > 1) {
72            warn "Found more than one release with discid $discid and barcode $barcode\n";
73            return interactive_select_release($xpath, scalar @releases, $discid, @releases);
74        } elsif (!@releases) {
75            warn "Found no releases with discid $discid and barcode $barcode\n";
76            return;
77        }
78        return $releases[0];
79    } else {
80        my $release_count = $xpath->findvalue('count(//release)');
81        my @releases = $xpath->findnodes('//release');
82        return interactive_select_release($xpath, $release_count, $discid, @releases);
83    }
84}
85
86sub get_musicbrainz_info {
87    my $params = shift;
88
89    my $discid = $params->{discid};
90    my %info;
91
92    $info{MUSICBRAINZ_DISCID} = $discid;
93
94    my $xpath = XML::XPath->new();
95    my $xml = lookup_release($discid) || return;
96   
97    $xpath->set_xml($xml);
98
99    # use the VorbisComment names from here http://musicbrainz.org/doc/MusicBrainz_Picard/Tags/Mapping
100
101    #my $release = $releases[0];
102    my $release = select_release($xpath, $params);
103    return unless $release;
104
105    $info{MUSICBRAINZ_ALBUMID} = $xpath->findvalue('@id', $release)->value;
106    $info{ALBUM}               = $xpath->findvalue('title', $release)->value;
107    @info{qw{ALBUMARTIST ALBUMARTISTSORT}} = get_artist_credits($xpath, $release);
108    $info{DATE}                = $xpath->findvalue('date', $release)->value;
109    $info{ORIGINALDATE}        = $xpath->findvalue('release-group/first-release-date', $release)->value;
110    $info{LANGUAGE}            = $xpath->findvalue('text-representation/language', $release)->value;
111    $info{SCRIPT}              = $xpath->findvalue('text-representation/script', $release)->value;
112
113    # select the proper medium (important for multidisc releases)
114    my ($medium) = $xpath->findnodes("medium-list/medium[disc-list/disc/\@id='$discid']", $release);
115
116    # disc position info
117    $info{DISCNUMBER} = $xpath->findvalue('position', $medium)->value;
118    $info{DISCTOTAL}  = $xpath->findvalue('../@count', $medium)->value;
119
120    #my $ua = LWP::UserAgent->new;
121    my $tracknum = 1;
122    for my $track_node ($xpath->findnodes('track-list/track', $medium)) {
123        my $prefix = sprintf('TRACK%02d', $tracknum);
124
125        $info{"$prefix.MUSICBRAINZ_TRACKID"} = $xpath->findvalue('@id', $track_node)->value;
126
127        my ($recording) = $xpath->findnodes('recording', $track_node);
128        $info{"$prefix.MUSICBRAINZ_RECORDINGID"} = $xpath->findvalue('@id', $recording)->value;
129        $info{"$prefix.TITLE"} = $xpath->findvalue('title', $recording)->value;
130        @info{"$prefix.ARTIST", "$prefix.ARTISTSORT"} = get_artist_credits($xpath, $recording);
131
132        #my $uri = URI->new("http://musicbrainz.org/ws/2/recording/$recording_mbid");
133        #$uri->query_form(inc => 'artists');
134        #my $res = $ua->get($uri);
135        #die $res->decoded_content;
136
137        #TODO: get track relations (Covers, etc.)
138
139        $tracknum++;
140    }
141
142    return \%info;
143}
144
145sub get_artist_credits {
146    my ($xpath, $context_node) = @_;
147
148    # use the MusicBrainz join phrase to build up the multiple artist credits
149    my ($credit, $sort_credit) = ('', '');
150    for my $credit_node ($xpath->findnodes('artist-credit/name-credit', $context_node)) {
151        $credit      .= $xpath->findvalue('concat(artist/name, @joinphrase)', $credit_node)->value;
152        $sort_credit .= $xpath->findvalue('concat(artist/sort-name, @joinphrase)', $credit_node)->value;
153    }
154
155    return ($credit, $sort_credit);
156}
157
158# module return
1591;
Note: See TracBrowser for help on using the repository browser.