X-Git-Url: https://git.draconx.ca/gitweb/mpdhacks.git/blobdiff_plain/44faac243210fd4ab72702a9acc2ff6cf7743d55..fb396c175b1673579630ba4e69c9b363c1f2ae1b:/mpdmenu.pl diff --git a/mpdmenu.pl b/mpdmenu.pl index 3cc0e35..2b5fcad 100755 --- a/mpdmenu.pl +++ b/mpdmenu.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl # -# Copyright © 2008,2010,2012,2020 Nick Bowler +# Copyright © 2008,2010,2012,2020-2021 Nick Bowler # # Silly little script to generate an FVWM menu with various bits of MPD # status information and controls. @@ -37,7 +37,7 @@ my $SELF = "$FindBin::Bin/$FindBin::Script"; my $MUSIC = $ENV{MUSIC} // "/srv/music"; my $sock; -my ($albumid, $trackid); +my ($albumid, $albumname, $trackid, $recordingid); my ($topmenu, $menu); my $mode = "top"; my %artistids; @@ -228,7 +228,13 @@ sub top_track_title { my @submenu; my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_RELEASETRACKID"); - @submenu = make_submenu("$menu-$mbid", "--track-id=$mbid") if $mbid; + 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}"), @@ -257,9 +263,19 @@ sub top_track_artist { 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"); - my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_ALBUMID"); - @submenu = make_submenu("$menu-$mbid", "--album-id=$mbid") if $mbid; + @submenu = make_submenu("$menu-$mbid", "--album-name=$album", + map { "--artist-id=$_" } @a); + + } fvwm_cmd("AddToMenu", $menu, fvwm_label_escape("Album:\t$entry->{Album}"), @@ -308,7 +324,7 @@ sub get_tracks_by_track_mbid { my $entry; return \%matches unless ($mbid); - MPD::exec("search", "(MUSICBRAINZ_RELEASETRACKID == \"$mbid\")"); + MPD::exec("search", "($tagname == \"$mbid\")"); while (<$sock>) { last if (/^OK/); die($_) if (/^ACK/); @@ -332,6 +348,10 @@ sub get_tracks_by_track_mbid { return \%matches; } +sub get_tracks_by_recording_mbid { + return get_tracks_by_track_mbid($_[0], "MUSICBRAINZ_TRACKID"); +} + # 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 { @@ -362,13 +382,28 @@ sub get_tracks_by_release_mbid { return \%matches; } -# Given an artist MBID, return a hash reference containing associated -# releases in the MPD database. The hash keys are release MBIDs. +# Insert the given entry into the referenced hash if it represents a +# standalone recording (not associated with a release). The recording +# MBID is used as the hash key. +sub check_standalone { + my ($outhash, $entry) = @_; + my ($mbid) = get_track_metadata($entry, "MUSICBRAINZ_TRACKID"); + + return if exists $entry->{MUSICBRAINZ_ALBUMID}; + $outhash->{$mbid} = $entry if ($mbid); +} + +# Given an artist MBID, return a list of two hash refererences. The +# first contains the associated releases in the MPD database and the +# hash keys are release MBIDs. The second contains the artist's +# standalone recordings and the hash keys are recording MBIDs. +# +# In scalar context only the release hash is returned. # # 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 (%releases, %standalones); my $entry; foreach my $mbid (@_) { @@ -379,6 +414,7 @@ sub get_releases_by_artist_mbid { if (/^(\w+): (.*)$/) { if ($1 eq "file") { + check_standalone(\%standalones, $entry); $entry = {}; } elsif ($1 eq "MUSICBRAINZ_ALBUMID") { $releases{$2} //= $entry; @@ -387,9 +423,10 @@ sub get_releases_by_artist_mbid { add_track_metadata($entry, $1, $2); } } + check_standalone(\%standalones, $entry); } - return \%releases; + return wantarray ? (\%releases, values %standalones) : \%releases; } # Given a filename, return the IDs (if any) for that file in the @@ -411,6 +448,24 @@ sub get_ids_by_filename { return @results; } +sub update_entry_ids { + my @notqueued = (); + + foreach my $entry (@_) { + unless (exists $entry->{Id}) { + my ($id) = get_ids_by_filename($entry->{file}); + if (defined $id) { + $entry->{Id} = $id; + } else { + push @notqueued, $entry; + next; + } + } + } + + return @notqueued; +} + # albumsort(matches, a, b) # # Sort hash keys (a, b) by disc/track number for album menus. @@ -444,8 +499,8 @@ sub menu_trackname { sub print_version { print < \$MPD::host, - 'port|p=s' => \$MPD::port, - 'menu|m=s' => \$menu, + 'host|h=s' => \$MPD::host, + 'port|p=s' => \$MPD::port, + 'menu|m=s' => \$menu, - 'artist-id=s' => sub { $artistids{$_[1]} = 1; $mode = "artist"; }, - 'album-id=s' => sub { $albumid = $_[1]; $mode = "album"; }, - 'track-id=s' => sub { $trackid = $_[1]; $mode = "track"; }, + 'artist-id=s' => sub { $artistids{$_[1]} = 1; $mode = "artist"; }, + 'album-id=s' => sub { $albumid = $_[1]; $mode = "album"; }, + 'album-name=s' => sub { $albumname = $_[1]; $mode = "albumname"; }, + 'track-id=s' => sub { $trackid = $_[1]; $mode = "track"; }, + 'recording-id=s' => sub { $recordingid = $_[1]; $mode = "recording"; }, - 'V|version' => sub { print_version(); exit }, - 'H|help' => sub { print_help(); exit }, + 'V|version' => sub { print_version(); exit }, + 'H|help' => sub { print_help(); exit }, - 'topmenu=s' => \$topmenu, # top menu name (for submenu generation) + 'topmenu=s' => \$topmenu, # top menu name (for submenu generation) ) or do { print_usage; exit 1 }; +$mode = "albumname" if ($albumname && $mode eq "artist"); + unless (defined $menu) { $topmenu //= "MenuMPD"; $menu = $topmenu . ($mode ne "top" ? $mode : ""); @@ -613,15 +677,17 @@ if ($mode eq "top") { } } elsif ($mode eq "artist") { # Create an artist menu. - my $matches = get_releases_by_artist_mbid(keys %artistids); - my $entry; + my ($matches, @recs) = get_releases_by_artist_mbid(keys %artistids); $menu //= "MenuMPDArtist"; my @mbids = sort { datesort($matches, $a, $b) } keys %$matches; my @files = map { $matches->{$_}->{file} } @mbids; my @thumbs = get_item_thumbnails({ small => 1 }, @files); - fvwm_cmd("AddToMenu", $menu, "No releases found", "Title") unless @mbids; + + unless (@mbids) { + fvwm_cmd("AddToMenu", $menu, "No releases found", "Title") + } foreach my $mbid (@mbids) { my $entry = $matches->{$mbid}; @@ -631,13 +697,53 @@ if ($mode eq "top") { "--album-id=$mbid"); fvwm_cmd("AddToMenu", $menu, $thumb . fvwm_label_escape($entry->{Album}), - @submenu); + @submenu); + } + + my @artists = map { "--artist-id=$_" } keys %artistids; + my %nonalbums = map { $_->{Album} => $_ } @recs; + foreach my $name (sort keys %nonalbums) { + my $mbid = $nonalbums{$name}->{MUSICBRAINZ_TRACKID}; + my @submenu = make_submenu("$topmenu-$mbid", @artists, + "--album-name=$name"); + fvwm_cmd("AddToMenu", $menu, fvwm_label_escape($name), @submenu); + } +} elsif ($mode eq "albumname") { + # Create a standalone recordings menu + my ($releases, @recs) = get_releases_by_artist_mbid(keys %artistids); + + $menu //= "MenuMPDRecordings"; + my @tracks = sort { $a->{Title} cmp $b->{Title} } + grep { $_->{Album} eq $albumname } @recs; + my @notqueued = update_entry_ids(@tracks); + + fvwm_cmd("AddToMenu", $menu); + fvwm_cmd("+", "No tracks found", "Title") unless @tracks; + + foreach my $entry (@tracks) { + next unless exists $entry->{Id}; + + fvwm_cmd("+", menu_trackname($entry), "Exec", + "exec", "$FindBin::Bin/mpdexec.pl", + "playid", $entry->{Id}); + } + + fvwm_cmd("+", "Not in play queue", "Title") if @notqueued; + foreach my $entry (@notqueued) { + fvwm_cmd("+", menu_trackname($entry)); } -} elsif ($mode eq "track") { - my $matches = get_tracks_by_track_mbid($trackid); +} elsif ($mode eq "track" || $mode eq "recording") { + my ($matches, $mbid); my @notqueued; + if ($mode eq "track") { + $matches = get_tracks_by_track_mbid($trackid) + } else { + $matches = get_tracks_by_recording_mbid($recordingid) + } + $menu //= "MenuMPDTrack"; + fvwm_cmd("DestroyMenu", $menu); my @files = sort { datesort($matches, $a, $b) } keys %$matches; my @thumbs = get_item_thumbnails({ small => 1 }, @files);