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

Last change on this file since 43 was 43, checked in by peter, 10 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.