]> git.draconx.ca Git - mpdhacks.git/blobdiff - mpdmenu.pl
mpdmenu: Use MBIDs for track matching instead of name matching.
[mpdhacks.git] / mpdmenu.pl
index 5968843a0bfb5c8092dc71bafccd08eb06b6ca08..8e597e7f9c704c3b585ecd29043aace22fe61b35 100755 (executable)
@@ -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";
-}