]> diplodocus.org Git - flac-archive/blob - fa-mb
test mp3::cd::read_args
[flac-archive] / fa-mb
1 #!/usr/local/bin/perl
2
3 =head1 NAME
4
5 B<fa-mb> - Query MusicBrainz
6
7 =head1 SYNOPSIS
8
9 B<fa-mb> B<-Q> B<ARTIST> B<TITLE>
10 B<fa-mb> [B<-p>] B<RELEASE-ID>
11
12 =head1 DESCRIPTION
13
14 The first form queries for releases by B<ARTIST> and B<TITLE>, listing a
15 summary, one per line. The first column is the id, which is used as the
16 B<RELEASE-ID> argument to the second form.
17
18 The second form looks up a release by B<RELEASE-ID>, listing all the tags for
19 each disc in the release in the form expected by B<fa-rip>.
20
21 The second form accepts a B<-p> option which enables the PAREN HACK, which is
22 quite gross. I like to put information such as "(live)" in the VERSION tag
23 rather than TITLE tag. This information comes out of MusicBrainz parenthesized
24 at the end of the track title, so this just strips out any parenthesized parts.
25 Frequently it is incorrect: pay attention!
26
27 The tags listing also hacks various Unicode characters that work just fine with
28 their ASCII equivalents, e.g. quotation marks.
29
30 =cut
31
32 package epg::flac::archive::mb;
33
34 use v5.12;
35 use utf8;
36 use warnings;
37
38 use Exporter 'import';
39 use JSON::PP;
40
41 use URI::Escape;
42
43 our @EXPORT_OK = qw[
44 format_tags
45 list_releases
46 read_releases
47 read_release
48 ];
49
50 sub tracks {
51 local $_;
52 my $tracks = shift;
53 [
54 map {
55 $_ = $_->{title};
56 # unicode hacks
57 s/‐/-/g;
58 s/’/'/g;
59 $_
60 } @$tracks
61 ]
62 }
63
64 sub read_release {
65 local $_;
66 my $parsed = decode_json(shift);
67 my $release = release_metadata($parsed);
68 # TODO this is dumb. put it into release_metadata
69 # TODO only include "format": "CD" (i.e. exclude "format": "DVD-Video")
70 $release->{discs} = [map { tracks($_->{tracks}) } @{$parsed->{media}}];
71 $release
72 }
73
74 sub release_metadata {
75 local $_ = shift;
76 my $date = $_->{date} // 'UNKNOWN';
77 my $events = $_->{'release-events'};
78 if (defined($events)) {
79 if (@$events > 1) {
80 warn('found example of multiple release events!');
81 }
82 my $event_date = $events->[0]->{date};
83 if (defined($event_date)) {
84 if ($event_date ne $date) {
85 warn("release-events has date $event_date (vs. $date)");
86 }
87 }
88 }
89 my ($discs, $tracks) = (0, 0);
90 for my $m (@{$_->{media} // []}) {
91 $discs += $m->{'disc-count'} // 0;
92 $tracks += $m->{'track-count'} // 0;
93 }
94 if ($tracks == 0 && defined($_->{'disc-count'})) {
95 $tracks = $_->{'disc-count'};
96 }
97 if (defined($_->{'disc-count'})) {
98 warn('disc count defined at release root!');
99 }
100 my $artist;
101 my $artcred = $_->{'artist-credit'};
102 if (defined($artcred)) {
103 $artist = join('', map {
104 # warn('HEYEPG! ', join(', ', keys(%$_)));
105 # warn("HEYEPG ", $_->{name}, 'join=', ($_->{joinphrase} // ''));
106 $_->{name}, ($_->{joinphrase} // '') } @$artcred);
107 } else {
108 warn('OH!');
109 }
110 {
111 id => $_->{id},
112 artist => $artist // 'UNKNOWN',
113 title => $_->{title},
114 discs => $discs,
115 tracks => $tracks,
116 country => $_->{country} // '--',
117 date => $date,
118 labels => join(', ', map { $_->{label}->{name} // 'UNKNOWN' } @{$_->{'label-info'} // []}),
119 disambiguation => $_->{disambiguation} // '',
120 status => $_->{status},
121 }
122 }
123
124 sub read_releases {
125 local $_;
126 my $parsed = decode_json(shift);
127 [map { release_metadata($_) } @{$parsed->{releases}}]
128 }
129
130 sub list_releases {
131 local $_;
132 my $releases = shift;
133 join("\n",
134 map {
135 sprintf('%s %-10s %3d tracks / %2d discs in %s %s by %s; %s, %s',
136 $_->{id}, $_->{date},
137 $_->{tracks}, $_->{discs},
138 $_->{country}, $_->{title}, $_->{labels}
139 , $_->{disambiguation}, $_->{status},
140 )
141 } @$releases)
142 }
143
144 sub format_tags {
145 local $_;
146 my %parm = @_;
147 my $hack_parens = $parm{hack_parens} // 0;
148 my $release = $parm{release};
149 my $artist = 'ARTIST=' . $release->{artist};
150 my $album = 'ALBUM=' . $release->{title};
151 my $date = 'DATE=' . $release->{date};
152 my $discs = $release->{discs};
153 my $multi = @$discs > 1;
154 my $d = 0;
155 join("\n",
156 map {
157 my @discnum;
158 if ($multi) {
159 $d++;
160 @discnum = ("DISCNUMBER=$d");
161 }
162 my $t = 0;
163 (
164 $artist, $album, $date, @discnum,
165 map {
166 $t++;
167 my @tags;
168 if ($hack_parens) {
169 /([^(]+)(.*)/;
170 my $title = $1;
171 my $version = $2;
172 if (length($version)) {
173 $title =~ s/\s+$//;
174 $version =~ s/^\(//;
175 $version =~ s/\)$//;
176 @tags = ("TITLE[$t]=$title", "VERSION[$t]=$version");
177 } else {
178 @tags = ("TITLE[$t]=$title")
179 }
180 } else {
181 @tags = ("TITLE[$t]=$_");
182 }
183 @tags
184 } @$_
185 )
186 } @$discs
187 )
188 }
189
190 sub main {
191 my $arg = shift || die;
192 if ($arg eq '-Q') {
193 my $artist = shift || die;
194 my $title = shift || die;
195 my $url =
196 'https://musicbrainz.org/ws/2/release?query='
197 . uri_escape("artist:\"$artist\" AND \"$title\" AND format:CD");
198 open(my $fh, '-|', 'curl', '--silent', '-Haccept:application/json', $url)
199 || die;
200 my @body;
201 while (<$fh>) {
202 push(@body, $_);
203 }
204 say(list_releases(read_releases(join('', @body))));
205 } else {
206 my $hack_parens;
207 if ($arg eq '-p') {
208 $arg = shift || die;
209 $hack_parens = 1;
210 } else { $hack_parens = 0 }
211 my $url = "https://musicbrainz.org/ws/2/release/$arg?inc=artist-credits+recordings";
212 open(my $fh, '-|', 'curl', '--silent', '-Haccept:application/json', $url)
213 || die;
214 say(format_tags(
215 hack_parens => $hack_parens,
216 release => read_release(join('', <$fh>)),
217 ));
218 }
219 0
220 }
221
222 if (!caller) {
223 exit(main(@ARGV))
224 }
225
226 1;
227
228 # Local variables:
229 # perl-indent-level: 8
230 # indent-tabs-mode: t
231 # End: