+# Generate the cover art entry in the top menu.
+sub top_track_cover {
+ my ($entry) = @_;
+
+ ($entry->{thumb}) = get_item_thumbnails($entry->{file});
+ print "$entry->{thumb}\n";
+ if ($entry->{thumb}) {
+ my $file = "$MUSIC/$entry->{file}";
+ my $cover = mpd_cover_filename($file);
+
+ $cover = fvwm_shell_literal($cover // $file);
+ fvwm_cmd_unquoted("AddToMenu", escape($menu),
+ escape($entry->{thumb}),
+ "Exec", "exec", "geeqie", $cover);
+ }
+}
+
+# Generate the "Title:" entry in the top menu.
+sub top_track_title {
+ my ($entry) = @_;
+
+ my @submenu = make_submenu("$menu-TopTrack",
+ "--title=$entry->{Title}");
+
+ fvwm_cmd("AddToMenu", $menu,
+ fvwm_label_escape("Title:\t$entry->{Title}"),
+ @submenu);
+}
+
+# Generate the "Artist:" entry in the top menu.
+sub top_track_artist {
+ my ($entry) = @_;
+ my @submenu;
+
+ # TODO: multi-artist tracks should get multiple artist menus; for now
+ # just combine the releases from all artists.
+ my @mbids = get_track_metadata($entry, "MUSICBRAINZ_ARTISTID");
+ if (@mbids) {
+ @submenu = make_submenu("$menu-TopArtist",
+ map { "--artist-id=$_" } @mbids);
+ }
+
+ fvwm_cmd("AddToMenu", $menu,
+ fvwm_label_escape("Artist:\t$entry->{Artist}"),
+ @submenu);
+}
+
+# Generate the "Album:" entry in the top menu.
+sub top_track_album {
+ my ($entry) = @_;
+ my @submenu;
+
+ my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_ALBUMID");
+ @submenu = make_submenu("$menu-$mbid", "--album-id=$mbid") if $mbid;
+
+ fvwm_cmd("AddToMenu", $menu,
+ fvwm_label_escape("Album:\t$entry->{Album}"),
+ @submenu);
+}
+
+# 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 {
+ my ($mbid) = @_;
+ my %matches;
+ my $entry;
+
+ return \%matches unless ($mbid);
+ mpd_exec("search", "(MUSICBRAINZ_ALBUMID == \"$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 an artist MBID, return a hash reference containing associated
+# releases in the MPD database. The hash keys are release MBIDs.
+#
+# Since MPD returns results on a per-track basis, each entry in the
+# hash has the metadata for one unspecified track from that release.
+sub get_releases_by_artist_mbid {
+ my %releases;
+ my $entry;
+
+ foreach my $mbid (@_) {
+ mpd_exec("search", "(MUSICBRAINZ_ARTISTID == \"$mbid\")");
+ while (<$sock>) {
+ last if (/^OK/);
+ die($_) if (/^ACK/);
+
+ if (/^(\w+): (.*)$/) {
+ if ($1 eq "file") {
+ $entry = {};
+ } elsif ($1 eq "MUSICBRAINZ_ALBUMID") {
+ $releases{$2} //= $entry;
+ }
+
+ add_track_metadata($entry, $1, $2);
+ }
+ }
+ }
+
+ return \%releases;
+}
+
+# Given a filename, return the IDs (if any) for that file in the
+# current MPD play queue.
+sub get_ids_by_filename {
+ my ($file) = @_;
+ my @results = ();
+
+ mpd_exec("playlistfind", "file", $file);
+ while (<$sock>) {
+ last if (/^OK/);
+ die($_) if (/^ACK/);
+
+ if (/^(\w+): (.*)$/) {
+ push @results, $2 if ($1 eq "Id");
+ }
+ }
+
+ return @results;
+}
+
+# albumsort(matches, a, b)
+#
+# Sort hash keys (a, b) by disc/track number for album menus.
+sub albumsort {
+ my ($matches, $a, $b) = @_;
+
+ return $matches->{$a}->{Disc} <=> $matches->{$b}->{Disc}
+ || $matches->{$a}->{Track} <=> $matches->{$b}->{Track}
+ || $a cmp $b;
+}
+
+# datesort(matches, a, b)
+#
+# Sort hash keys (a, b) by release date
+sub datesort {
+ my ($matches, $a, $b) = @_;
+
+ return $matches->{$a}->{Date} cmp $matches->{$b}->{Date}
+ || $a cmp $b;
+}
+
+# menu_trackname(entry)
+#
+# Format the track name for display in an FVWM menu, where entry
+# is a hash reference containing the track metadata.
+sub menu_trackname {
+ my ($entry) = @_;
+ my $fmt = "$entry->{trackfmt}$entry->{Artist} - $entry->{Title}";
+ return "$entry->{thumb}" . fvwm_label_escape($fmt);