X-Git-Url: https://git.draconx.ca/gitweb/mpdhacks.git/blobdiff_plain/020f9f800a804c638d6432711be319c5b9bfb988..6b2e89cb66902820a6b5e31991bad3ae4c70296b:/mpdmenu.pl diff --git a/mpdmenu.pl b/mpdmenu.pl index 5968843..8e597e7 100755 --- a/mpdmenu.pl +++ b/mpdmenu.pl @@ -37,7 +37,7 @@ my $host = $ENV{MPD_HOST} // "localhost"; my $port = $ENV{MPD_PORT} // "6600"; my $sock; -my ($albumid, $title); +my ($albumid, $trackid); my %artistids; my $menu; my $mode = "top"; @@ -252,9 +252,10 @@ sub top_track_cover { # 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}"), @@ -292,6 +293,72 @@ sub top_track_album { @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 { @@ -430,6 +497,7 @@ Options: -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 @@ -442,7 +510,7 @@ GetOptions( '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 }, @@ -596,114 +664,40 @@ if ($mode eq "top") { @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"; -}