+# get_item_thumbnails({ options }, file, ...)
+# get_item_thumbnails(file, ...)
+#
+# For each music file listed, obtain a thumbnail (if any) for the cover art.
+#
+# The first argument is a hash reference to control the mode of operation;
+# it may be omitted for default options.
+#
+# get_item_thumbnails({ small => 1 }, ...) - smaller thumbnails
+#
+# The returned list consists of strings (in the same order as the filename
+# arguments) suitable for use directly in FVWM menus; by default the filename
+# is bracketed by asterisks (e.g., "*thumbnail.png*"); in small mode it is
+# surrounded by % (e.g., "%thumbnail.png%"). If no cover art was found, the
+# empty string is returned for that file.
+sub get_item_thumbnails {
+ my @results = ();
+ my $flags = {};
+ my @opts = ();
+
+ $flags = shift if (reftype($_[0]) eq "HASH");
+ return @results unless @_;
+
+ my $c = "*";
+ if ($flags->{small}) {
+ push @opts, "--small";
+ $c = "%";
+ }
+
+ if ($mpd_have_binarylimit) {
+ # --embedded implies and requires binarylimit support
+ push @opts, "--embedded";
+ } else {
+ push @opts, "--no-binarylimit";
+ }
+
+ open THUMB, "-|", "$FindBin::Bin/mpdthumb.sh", @opts, "--", @_;
+ foreach (@_) {
+ my $thumb = <THUMB>;
+ chomp $thumb;
+
+ $thumb = "$c$thumb$c" if (-f $thumb);
+ push @results, $thumb;
+ }
+ close THUMB;
+ die("mpdthumb failed") if ($?);
+
+ return @results;
+}
+
+# add_track_metadata(hashref, key, value)
+#
+# Inserts the given key into the referenced hash; if the key already exists
+# in the hash then the hash element is converted to an array reference (if
+# it isn't already) and the value is appended to that array.
+sub add_track_metadata {
+ my ($entry, $key, $value) = @_;
+
+ if (exists($entry->{$key})) {
+ my $ref = $entry->{$key};
+
+ if (reftype($ref) ne "ARRAY") {
+ return if ($ref eq $value);
+
+ $ref = [$ref];
+ $entry->{$key} = $ref;
+ }
+
+ push(@$ref, $value) unless (any {$_ eq $value} @$ref);
+ } else {
+ $entry->{$key} = $value;
+ }
+}
+
+# get_track_metadata(hashref, key)
+#
+# Return the values associated with the given metadata key as a list.
+sub get_track_metadata {
+ my ($entry, $key) = @_;
+
+ return () unless (exists($entry->{$key}));
+
+ my $ref = $entry->{$key};
+ return @$ref if (reftype($ref) eq "ARRAY");
+ return $ref
+}
+
+# Given a music filename, search for the cover art in the same directory.
+sub mpd_cover_filename {
+ my ($dir) = @_;
+ my $file;
+
+ $dir =~ s/\/[^\/]*$//;
+ foreach ("cover.png", "cover.jpg", "cover.tiff", "cover.bmp") {
+ if (-f "$dir/$_") {
+ $file = "$dir/$_";
+ last;
+ }
+ }
+ return unless defined $file;
+
+ # Follow one level of symbolic link to get to the scans directory.
+ $file = readlink($file) // $file;
+ $file = "$dir/$file" unless ($file =~ /^\//);
+ return $file;
+}
+
+# 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", MPD::escape($menu),
+ MPD::escape($entry->{thumb}),
+ "Exec", "exec", "geeqie", $cover);
+ }
+}
+
+# Generate the "Title:" entry in the top menu.
+sub top_track_title {
+ my ($entry) = @_;
+ my @submenu;
+
+ my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_RELEASETRACKID");
+ if ($mbid) {
+ @submenu = make_submenu("$menu-$mbid", "--track-id=$mbid")
+ } else {
+ ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID");
+ @submenu = make_submenu("$menu-track-$mbid", "--recording-id=$mbid")
+ if ($mbid);
+ }
+
+ 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;
+
+ if (($mbid) = get_track_metadata($entry, "MUSICBRAINZ_ALBUMID")) {
+ @submenu = make_submenu("$menu-$mbid", "--album-id=$mbid");
+ } elsif (($mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID")) {
+ # Standalone recording
+ my @a = get_track_metadata($entry, "MUSICBRAINZ_ARTISTID");
+ my ($album) = get_track_metadata($entry, "Album");
+
+ @submenu = make_submenu("$menu-$mbid", "--album-name=$album",
+ map { "--artist-id=$_" } @a);
+
+ }
+
+ fvwm_cmd("AddToMenu", $menu,
+ fvwm_label_escape("Album:\t$entry->{Album}"),
+ @submenu);
+}
+
+# Generate the "MusicBrainz:" entry in the top menu.
+sub top_track_musicbrainz {
+ my ($entry) = @_;
+ my ($track_mbid, $recording_mbid, $release_mbid);
+ my @artist_mbids;
+ my $label = "MB:";
+ my %idmap;
+
+ ($track_mbid) = get_track_metadata($entry, "MUSICBRAINZ_RELEASETRACKID");
+ ($recording_mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID");
+ ($release_mbid) = get_track_metadata($entry, "MUSICBRAINZ_ALBUMID");
+ @artist_mbids = get_track_metadata($entry, "MUSICBRAINZ_ARTISTID");
+ return unless $track_mbid // $recording_mbid
+ // $release_mbid // @artist_mbids;
+
+ foreach (get_track_metadata($entry, "Comment")) {
+ $idmap{$1} = $2 if /^([^=]*)=(.*) \(idmap\)$/;
+ }
+
+ fvwm_cmd("AddToMenu", $menu, "", "Nop");
+ if ($track_mbid) {
+ fvwm_cmd("AddToMenu", $menu, "$label\tShow track",
+ "Exec", "exec", "xdg-open",
+ "https://musicbrainz.org/track/$track_mbid");
+ $label = "";
+ } elsif ($recording_mbid) {
+ fvwm_cmd("AddToMenu", $menu, "$label\tShow recording",
+ "Exec", "exec", "xdg-open",
+ "https://musicbrainz.org/recording/$recording_mbid");
+ $label = "";
+ } elsif ($release_mbid) {
+ fvwm_cmd("AddToMenu", $menu, "$label\tShow",
+ "Exec", "exec", "xdg-open",
+ "https://musicbrainz.org/release/$release_mbid");
+ $label = "";
+ }
+
+ foreach my $mbid (@artist_mbids) {
+ my $name = " $idmap{$mbid}" if $idmap{$mbid};
+
+ fvwm_cmd("AddToMenu", $menu, "$label\tShow artist$name",
+ "Exec", "exec", "xdg-open",
+ "https://musicbrainz.org/artist/$mbid");
+ $label = "";
+ }
+}
+
+# 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;