#!/usr/bin/perl use warnings; use strict; use utf8; use Encode; use open ":locale"; use LWP::UserAgent; use XML::LibXML; my $cuename; my $cueenc = ""; my $wavor; my @encops; for(@ARGV){ if(!length($cueenc) && /^:/){ $cueenc = $_; }elsif(!defined($cuename)){ $cuename = $_; }elsif(!defined($wavor)){ $wavor = $_; } } if(!defined($cuename)){ die "give cue"; } my @tracks; my $disc = {}; my $pt = $disc; my $cue; open($cue, "<$cueenc", $cuename) || die "cue open error"; while(<$cue>){ my @lt; while(/(?|([^\s"]+)|"((?:[^"]|(?<=(?<!\\)\\)")*)")/g){ push(@lt, $1); $lt[$#lt] =~ s/\\([\\"])/$1/g; } if($lt[0] eq "PERFORMER" || $lt[0] eq "TITLE"){ $pt->{lc($lt[0])} = $lt[1]; }elsif($lt[0] eq "FILE"){ $wavor = $lt[1] if(!defined($wavor)); }elsif($lt[0] eq "INDEX"){ if($lt[2] !~ /^([0-9]{2}):([0-9]{2}):([0-9]{2})$/){ die "index error"; } my $s = $1 * 60 + $2 + $3 / 75; if($lt[1] == 0){ $pt->{prgap} = $s; }elsif($lt[1] == 1){ $pt->{start} = $s; $pt->{prgap} = $s if(!defined($pt->{prgap})); } }elsif($lt[0] eq "TRACK"){ if($tracks[$lt[1]]){ die "track has same number"; } $pt = {num => 0+$lt[1]}; $tracks[$lt[1] - 1] = $pt; } } if(!defined($wavor)){ die "give audio file"; } my $probd; open($probd, "-|", "ffprobe", "-v", "0", "-show_streams", $wavor) || die; my $d = 1; my $dflags = 0; while(<$probd>){ if(/^time_base=([0-9]+)\/([0-9]+)$/){ $d *= $1 / $2; $dflags |= 1; }elsif(/^duration_ts=([0-9]+)$/){ $d *= $1; $dflags |= 2; } } close($probd); my $sectoffset = 150.5; my @tocid = (1, int(@tracks), int(75 * $d + $sectoffset)); my $lasp; for(@tracks){ push(@tocid, int(75 * $_->{prgap} + $sectoffset)); $lasp = $_->{prgap}; } if($dflags != 3 || $d < $lasp){ my $dlast = $lasp + $lasp / (@tracks - 1); $tocid[2] = int(75 * $dlast + $sectoffset); } my $ua = LWP::UserAgent->new(agent=>"cueask.pl-script/0.1 (by dyknon)"); my $dres = $ua->get("https://musicbrainz.org/ws/2/discid/-?toc=".join("+", @tocid)."&inc=recordings+artists"); if($dres->code != 200){ die "MusicBrainz API call error"; } my $ddom = XML::LibXML->load_xml(string=>$dres->content); my $dxc = XML::LibXML::XPathContext->new($ddom); $dxc->registerNs("m", "http://musicbrainz.org/ns/mmd-2.0#"); my @releases = $dxc->findnodes("/m:metadata/m:release-list/m:release"); if(!@releases){ die "Not found in MusicBrainz Database"; } print(int(@releases)." releases found.\n"); while(my ($n, $r) = each(@releases)){ print("release: https://musicbrainz.org/release/" . $dxc->findvalue("\@id", $r) . "\n"); print(" title: " . $dxc->findvalue("m:title[1]", $r) . "\n"); my @artists = $dxc->findnodes("m:artist-credit[1]/m:name-credit", $r); if(@artists){ print(" artist: "); for(@artists){ print($dxc->findvalue("m:artist/m:name", $_)); print($dxc->findvalue("\@joinphrase", $_)); } print("\n"); } my $possort = sub{ my $gpos = sub{ $dxc->findvalue("m:position", shift); }; sort{$gpos->($a) <=> $gpos->($b)}(@_); }; my @mediums = $possort->($dxc->findnodes("m:medium-list/m:medium", $r)); for my $m(@mediums){ print(" media " . $dxc->findvalue("m:position", $m) . "(" . $dxc->findvalue("m:format", $m) . "):\n"); for my $t($dxc->findnodes("m:track-list/m:track", $m)){ print(" track " . $dxc->findvalue("m:number", $t) . ": " . $dxc->findvalue("m:recording/m:title", $t) . "\n"); } } }