my $port = $ENV{MPD_PORT} // "6600";
my $sock;
-my ($albumid, $title);
+my ($albumid, $trackid);
my %artistids;
my $menu;
my $mode = "top";
# Generate the "Title:" entry in the top menu.
sub top_track_title {
my ($entry) = @_;
+ my @submenu;
- my @submenu = make_submenu("$menu-TopTrack",
- "--title=$entry->{Title}");
+ my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_RELEASETRACKID");
+ @submenu = make_submenu("$menu-$mbid", "--track-id=$mbid") if $mbid;
fvwm_cmd("AddToMenu", $menu,
fvwm_label_escape("Title:\t$entry->{Title}"),
@submenu);
}
+# Given a work MBID, return a hash reference containing all tracks
+# linked to that work. The hash keys are filenames.
+sub get_tracks_by_work_mbid {
+ my %matches;
+ my $entry;
+
+ foreach my $mbid (@_) {
+ mpd_exec("search", "(MUSICBRAINZ_WORKID == \"$mbid\")");
+ while (<$sock>) {
+ last if (/^OK/);
+ die($_) if (/^ACK/);
+
+ if (/^(\w+): (.*)$/) {
+ if ($1 eq "file") {
+ if (exists($matches{$2})) {
+ $entry = $matches{$2};
+ } else {
+ $entry = {};
+ $matches{$2} = $entry;
+ }
+ }
+
+ add_track_metadata($entry, $1, $2);
+ }
+ }
+ }
+
+ return \%matches;
+}
+
+# Given a track MBID, return a hash reference containing all "related"
+# tracks in the MPD database. The hash keys are filenames.
+#
+# Currently tracks are considered "related" if their associated recordings
+# have at least one work in common.
+sub get_tracks_by_track_mbid {
+ my ($mbid) = @_;
+ my %source;
+ my %matches;
+ my $entry;
+
+ return \%matches unless ($mbid);
+ mpd_exec("search", "(MUSICBRAINZ_RELEASETRACKID == \"$mbid\")");
+ while (<$sock>) {
+ last if (/^OK/);
+ die($_) if (/^ACK/);
+
+ if (/^(\w+): (.*)$/) {
+ add_track_metadata(\%source, $1, $2);
+ }
+ }
+
+ # Always include the current track
+ $matches{$source{file}} = \%source;
+
+ # Find all tracks related by work
+ foreach my $mbid (get_track_metadata(\%source, "MUSICBRAINZ_WORKID")) {
+ my $related = get_tracks_by_work_mbid($mbid);
+ foreach (keys %$related) {
+ $matches{$_} //= $related->{$_};
+ }
+ }
+
+ return \%matches;
+}
+
# Given a release MBID, return a hash reference containing all its
# associated tracks in the MPD database. The hash keys are filenames.
sub get_tracks_by_release_mbid {
-m, --menu=NAME Set the name of the generated menu.
--album-id=MBID Generate a menu for the given release MBID.
--artist-id=MBID Generate a menu for the given artist MBID.
+ --track-id=MBID Generate a menu for the given track MBID.
-V, --version Print a version message and then exit.
-H, --help Print this message and then exit.
EOF
'artist-id=s' => sub { $artistids{$_[1]} = 1; $mode = "artist"; },
'album-id=s' => sub { $albumid = $_[1]; $mode = "album"; },
- 'title=s' => sub { $title = $_[1]; $mode = "track"; },
+ 'track-id=s' => sub { $trackid = $_[1]; $mode = "track"; },
'V|version' => sub { print_version(); exit },
'H|help' => sub { print_help(); exit },
@submenu);
}
} elsif ($mode eq "track") {
- # Create a title menu.
- my @titles;
- my $entry;
-
- $menu = "MenuMPDTitle" unless defined $menu;
-
- # Open and close brackets.
- my ($ob, $cb) = ("[\[~〜<〈(ー−-]", "[\]~〜>〉)ー−-]");
-
- $_ = $title;
-
- # Deal with specific cases.
- s/ちいさな(?=ヘミソフィア)//; # ヘミソフィア
- s/ "mix on air flavor" dear EIKO SHIMAMIYA//; # Spiral wind
- s/ "So,you need me" Style//; # I need you
- s/ ::Symphony Second movement:://; # Disintegration
- s/-\[instrumental\]//; # 青い果実
- s/ -Practice Track-//; # Fair Heaven
- s/〜世界で一番アナタが好き〜//; # Pure Heart
- s/〜彼方への哀歌//; # 十二幻夢
- s/ sora no uta ver.//; # 美しい星
-
- s/\s*-remix-$//; # Otherwise "D-THREAD -remix-" doesn't work right.
+ my $matches = get_tracks_by_track_mbid($trackid);
+ my @notqueued;
- # Deal with titles like "blah (ABC version)".
- s/\s*$ob.*(style|mix|edit|edition|ver\.?|version|melody|カラオケ)$cb?$//i;
+ $menu //= "MenuMPDTrack";
- # Deal with titles like "blah (without XYZ)".
- s/\s*$ob\s*((e\.)?piano|english|japanese|inst|tv|without|w\/o|off|back|short|karaoke|game).*//i;
-
- # Deal with titles like "blah instrumental".
- s/\s+(instrumental|off vocal|short|tv)([\s-]+(mix|size|version))?$//i;
- s/\s+without\s+\w+$//i;
-
- # Deal with separate movements in classical pieces.
- s/: [IVX]+\..*//;
-
- my $basetitle = $_;
- my $_basetitle = $basetitle;
+ my @files = sort { datesort($matches, $a, $b) } keys %$matches;
+ my @thumbs = get_item_thumbnails({ small => 1 }, @files);
- $_basetitle =~ s/"/\\"/g;
- print $sock "playlistsearch title \"$_basetitle\"\n";
- while (<$sock>) {
- last if (/^OK/);
- die($_) if (/^ACK/);
+ fvwm_cmd("AddToMenu", $menu);
+ fvwm_cmd("+", "No tracks found", "Title") unless @files;
+ foreach my $file (@files) {
+ my $entry = $matches->{$file};
+ $entry->{thumb} = shift @thumbs;
- if (/^(\w+): (.*)$/) {
- if ($1 eq "file") {
- push @titles, $entry if (keys(%$entry) > 0);
- $entry = {};
+ unless (exists $entry->{Id}) {
+ my ($id) = get_ids_by_filename($file);
+ if (defined $id) {
+ $entry->{Id} = $id;
+ } else {
+ push @notqueued, $entry;
+ next;
}
-
- $entry->{$1} = $2;
}
- }
- push @titles, $entry if (keys(%$entry) > 0);
-
-{ # work around 'use locale' breaking s///i
- use locale;
- my @thumbs = get_item_thumbnails({ small => 1 },
- map { $_->{file} } @titles);
- for (my $i = 0; $i < @titles; $i++) {
- $titles[$i]->{thumb} = $thumbs[$i];
+ fvwm_cmd("+", menu_trackname($entry), "Exec",
+ "exec", "$FindBin::Bin/mpdexec.pl",
+ "playid", $entry->{Id});
}
- foreach (sort titlesort @titles) {
- my ($t_file, $t_artist, $t_title, $t_id, $thumb) = (
- $_->{file},
- $_->{Artist},
- $_->{Title},
- $_->{Id},
- $_->{thumb}
- );
-
- # MPD searches are case-insensitive.
- next if (!($t_title =~ m/(\P{Latin}|^)\Q$basetitle\E(\P{Latin}|$)/ || $t_title =~ m/\Q$basetitle\E/i));
-
- $t_artist = sanitise($t_artist, 1);
- $t_title = sanitise($t_title, 1);
-
- cmd("AddToMenu $menu \"$thumb$t_artist - $t_title\""
- ." Exec exec $FindBin::Bin/mpdexec.pl"
- ." playid $t_id");
+ fvwm_cmd("+", "Not in play queue", "Title") if @notqueued;
+ foreach my $entry (@notqueued) {
+ fvwm_cmd("+", menu_trackname($entry));
}
-} # end use locale workaround
}
# Finished.
print $sock "close\n";
-
-sub sanitise
-{
- $_ = $_[0];
- s/&/&&/g if ($_[1]);
- s/([\$@%^*])/\1\1/g;
- s/"/\\"/g;
- return $_;
-}
-
-sub titlesort
-{
- return ($a->{Album} cmp $b->{Album}) if($a->{Album} ne $b->{Album});
- return ($a->{Artist} cmp $b->{Artist}) if($a->{Artist} ne $b->{Artist});
- return ($a->{Title} cmp $b->{Title});
-}
-
-sub cmd
-{
- print "$_[0]\n";
-}