X-Git-Url: https://git.draconx.ca/gitweb/fvwmconf.git/blobdiff_plain/402a36125f4da1e4cce1ae651d2cd7af4211ff20..25a02c0e784c071a1fd46e38fc11f858f3c6bf64:/scripts/mpdmenu.pl diff --git a/scripts/mpdmenu.pl b/scripts/mpdmenu.pl deleted file mode 100755 index c36fad7..0000000 --- a/scripts/mpdmenu.pl +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/perl -# -# Copyright © 2018,2010,2012,2019 Nick Bowler -# -# Silly little script to generate an FVWM menu with various bits of MPD -# status information and controls. -# -# License WTFPL2: Do What The Fuck You Want To Public License, version 2. -# This is free software: you are free to do what the fuck you want to. -# There is NO WARRANTY, to the extent permitted by law. - -use strict; - -use Getopt::Long; -use IO::Socket::INET6; - -use constant { - MPD_MJR_MIN => 0, - MPD_MNR_MIN => 13, - MPD_REV_MIN => 0, -}; - -use utf8; -use open qw(:std :utf8); -binmode(STDOUT, ":utf8"); -use Encode; - -sub cmd -{ - print "$_[0]\n"; -} - -# Global hash for tracking what is to be "accepted". -my %accept = (); - -my $FVWM = (defined $ENV{FVWM_USERDIR}) ? $ENV{FVWM_USERDIR} - : $ENV{HOME}."/.fvwm"; -my $icons = "$FVWM/icons"; - -# Default values for stuff. -my ($album, $artist, $title, $menu) = (undef, undef, undef, undef); -my $host = (defined $ENV{MPD_HOST}) ? $ENV{MPD_HOST} : "localhost"; -my $port = (defined $ENV{MPD_PORT}) ? $ENV{MPD_PORT} : "6600"; - -GetOptions( - 'host|h=s' => \&host, # Host that MPD is running on. - 'port|p=s' => \&port, # Port that MPD is listening on. - 'menu|m=s' => \$menu, # Name of the menu to create. - 'album=s' => \$album, # Album to get tracks from - 'artist=s' => \$artist, # Artist to limit results to - 'title=s' => \$title, # Title to create menu for -); - -$album = decode_utf8($album) if defined($album); -$artist = decode_utf8($artist) if defined($artist); -$title = decode_utf8($title) if defined($title);; - -# Connect to MPD. -my $sock = new IO::Socket::INET6( - PeerAddr => $host, - PeerPort => $port, - Proto => 'tcp', - Timeout => 2 -) or die("could not open socket: $!.\n"); -binmode($sock, ":utf8"); - -die("could not connect to MPD: $!.\n") - if (!(<$sock> =~ /^OK MPD ([0-9]+)\.([0-9]+)\.([0-9]+)$/)); - -die("MPD version $1.$2.$3 insufficient.\n") - if ( ($1 < MPD_MJR_MIN) - || ($1 == MPD_MJR_MIN && $2 < MPD_MNR_MIN) - || ($1 == MPD_MJR_MIN && $2 == MPD_MNR_MIN && $3 < MPD_REV_MIN)); - -if (defined $album) { - # Create an album menu. - my @playlist = (); - my $entry; - - $menu = "MenuMPDAlbum" unless defined $menu; - - $album =~ s/"/\\"/g; - print $sock "playlistfind album \"$album\"\n"; - while (<$sock>) { - last if (/^OK/); - die($_) if (/^ACK/); - - if (/^(\w+): (.*)$/) { - if ($1 eq "file") { - if (keys(%$entry) > 0) { - addalbumentry(\@playlist, $entry) - } - - $entry = {}; - } - - $entry->{$1} = $2; - } - } - addalbumentry(\@playlist, $entry) if (keys(%$entry) > 0); - - die("No tracks found.\n") if (!@playlist); - foreach (sort albumsort @playlist) { - my ($t_file, $t_trackno, $t_artist, $t_title, $t_id) = ( - $_->{file}, - $_->{Track}, - $_->{Artist}, - $_->{Title}, - $_->{Id}, - ); - - next if (defined $artist && !$accept{albumdir($t_file)}); - - $t_artist = sanitise($t_artist, 0); - $t_title = sanitise($t_title, 0); - - my $cmd = sprintf "AddToMenu $menu \"%d\t%s - %s\"" - ." Exec exec $FVWM/scripts/mpdexec.pl" - ." playid %d", - $t_trackno, $t_artist, $t_title, $t_id; - - cmd($cmd); - } -} elsif (defined $artist) { - # Create an artist menu. - my %albums = (); - my $file; - my $quoteartist = $artist; - - $menu = "MenuMPDArtist" unless defined $menu; - - $quoteartist =~ s/"/\\"/g; - print $sock "playlistfind artist \"$quoteartist\"\n"; - while (<$sock>) { - last if (/^OK/); - die($_) if (/^ACK/); - - if (/^(\w+): (.*)$/) { - $file = $2 if ($1 eq "file"); - $albums{$2} = $file if ($1 eq "Album"); - } - } - - die("No albums found.\n") if (!keys(%albums)); - -{ # work around 'use locale' breaking s///i - my $i = 0; - use locale; - foreach (sort keys(%albums)) { - my $key = $_; - my $a_album = sanitise($key, 1); - - open THUMB, "-|", "$FVWM/scripts/thumbnail.zsh", - "--small", "--music", $albums{$key}; - my $thumb = ; - close THUMB; - die("Incompetent use of thumbnail.zsh") if ($?); - - $thumb =~ s/\n//sg; - $thumb = "%$thumb%" if (-f $thumb); - - cmd("AddToMenu $menu \"$thumb$a_album\" Popup MenuMPDArt_$i"); - - cmd("AddToMenu MenuMPDArt_$i DynamicPopUpAction MakeMenuMPDArt_$i"); - - cmd("DestroyFunc MakeMenuMPDArt_$i"); - cmd("AddToFunc MakeMenuMPDArt_$i - + I DestroyMenu MenuMPDArt_$i - + I -PipeRead \"exec $FVWM/scripts/mpdmenu.pl " - ."--menu MenuMPDArt_$i " - ."--album ".shellify($key, 1)." " - ."--artist ".shellify($artist, 1)."\""); - - cmd("AddToFunc KillMenuMPD I DestroyMenu MenuMPDArt_$i"); - cmd("AddToFunc KillMenuMPD I DestroyFunc MakeMenuMPDArt_$i"); - - $i++; - } -} # end use locale workaround -} elsif (defined $title) { - # 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. - - # Deal with titles like "blah (ABC version)". - s/\s*$ob.*(style|mix|edit|edition|ver\.?|version|melody|カラオケ)$cb?$//i; - - # 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; - - $_basetitle =~ s/"/\\"/g; - print $sock "playlistsearch title \"$_basetitle\"\n"; - while (<$sock>) { - last if (/^OK/); - die($_) if (/^ACK/); - - if (/^(\w+): (.*)$/) { - if ($1 eq "file") { - push @titles, $entry if (keys(%$entry) > 0); - $entry = {}; - } - - $entry->{$1} = $2; - } - } - push @titles, $entry if (keys(%$entry) > 0); - -{ # work around 'use locale' breaking s///i - use locale; - foreach (sort titlesort @titles) { - my ($t_file, $t_artist, $t_title, $t_id) = ( - $_->{file}, - $_->{Artist}, - $_->{Title}, - $_->{Id}, - ); - - # 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); - - open THUMB, "-|", "$FVWM/scripts/thumbnail.zsh", - "--small", "--music", $t_file; - my $thumb = ; - close(THUMB); - die("Incompetent use of thumbnail.zsh") if ($?); - - $thumb =~ s/\n//sg; - $thumb = "%$thumb%" if (-f $thumb); - - cmd("AddToMenu $menu \"$thumb$t_artist - $t_title\"" - ." Exec exec $FVWM/scripts/mpdexec.pl" - ." playid $t_id"); - } -} # end use locale workaround -} else { - # Make MPD base menu - my ($state, $songid) = (undef, undef); - my %entry = (); - - $menu = "MenuMPD" unless defined $menu; - - print $sock "status\n"; - while (<$sock>) { - last if (/^OK/); - die($_) if (/^ACK/); - - if (/^(\w+): (.*)$/) { - $state = $2 if ($1 eq "state"); - $songid = $2 if ($1 eq "songid"); - } - } - die("Failed status query\n") unless (defined $state); - - cmd("AddToMenu $menu Playing Title") if ($state eq "play"); - cmd("AddToMenu $menu Paused Title") if ($state eq "pause"); - cmd("AddToMenu $menu Stopped Title") if ($state eq "stop"); - - if (defined $songid) { - print $sock "playlistid $songid\n"; - while (<$sock>) { - last if (/^OK/); - die($_) if (/^ACK/); - - if (/^(\w+): (.*)$/) { - $entry{$1} = $2; - } - } - die("Failed data query\n") unless (keys(%entry) > 0); - - open THUMB, "-|", "$FVWM/scripts/thumbnail.zsh", - "--image", "--music", $entry{file}; - my $thumb = ; - my $scan = ; - close(THUMB); - die("Incompetent use of thumbnail.sh") if ($?); - - $thumb =~ s/\n//sg; - $scan =~ s/\n//sg; - - if (-f $thumb) { - cmd("AddToMenu $menu \"*$thumb*\" " - ."Exec exec geeqie ".shellify($scan, 0)); - } - cmd("AddToMenu $menu \"Title: ".sanitise($entry{Title}, 0) - ."\" Popup MenuMPDTitle"); - cmd("AddToMenu $menu \"Artist: ".sanitise($entry{Artist}, 0) - ."\" Popup MenuMPDArtist"); - cmd("AddToMenu $menu \"Album: ".sanitise($entry{Album}, 0) - ."\" Popup MenuMPDAlbum"); - cmd("AddToMenu $menu \"\" Nop"); - } else { - cmd("AddToMenu $menu \"\""); - cmd("AddToMenu $menu \"\" Nop"); - } - - if ($state eq "play" || $state eq "pause") { - cmd("AddToMenu $menu \"\t\tNext%$icons/next.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl next"); - cmd("AddToMenu $menu \"\t\tPause%$icons/pause.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl pause"); - cmd("AddToMenu $menu \"\t\tPlay%$icons/play.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl play"); - cmd("AddToMenu $menu \"\t\tStop%$icons/stop.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl stop"); - cmd("AddToMenu $menu \"\t\tPrev%$icons/prev.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl previous"); - } elsif ($state eq "stop") { - cmd("AddToMenu $menu \"\t\tPlay%$icons/play.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl play"); - } else { - die("Unknown MPD state!\n"); - } - - cmd("AddToMenu $menu \"\" Nop"); - cmd("AddToMenu $menu \"\t\tShuffle%$icons/shuffle.svg:16x16%\" " - ."Exec exec $FVWM/scripts/mpdexec.pl shuffle"); - - cmd("DestroyMenu MenuMPDTitle"); - cmd("AddToMenu MenuMPDTitle DynamicPopUpAction MakeMenuMPDTitle"); - cmd("DestroyMenu MenuMPDArtist"); - cmd("AddToMenu MenuMPDArtist DynamicPopUpAction MakeMenuMPDArtist"); - cmd("DestroyMenu MenuMPDAlbum"); - cmd("AddToMenu MenuMPDAlbum DynamicPopUpAction MakeMenuMPDAlbum"); - - cmd("DestroyFunc MakeMenuMPDTitle"); - cmd("AddToFunc MakeMenuMPDTitle - + I DestroyMenu MenuMPDTitle - + I -PipeRead \"exec $FVWM/scripts/mpdmenu.pl " - ."--menu MenuMPDTitle " - ."--title ".shellify($entry{Title}, 1)."\""); - - cmd("DestroyFunc MakeMenuMPDAlbum"); - cmd("AddToFunc MakeMenuMPDAlbum - + I DestroyMenu MenuMPDAlbum - + I -PipeRead \"exec $FVWM/scripts/mpdmenu.pl " - ."--menu MenuMPDAlbum " - ."--album ".shellify($entry{Album}, 1)." " - ."--artist ".shellify($entry{Artist}, 1)."\""); - - cmd("DestroyFunc MakeMenuMPDArtist"); - cmd("AddToFunc MakeMenuMPDArtist - + I DestroyMenu MenuMPDArtist - + I -PipeRead \"exec $FVWM/scripts/mpdmenu.pl " - ."--menu MenuMPDArtist " - ."--artist ".shellify($entry{Artist}, 1)."\""); - - cmd("DestroyFunc KillMenuMPD"); - cmd("AddToFunc KillMenuMPD I Nop"); -} - -# Finished. -print $sock "close\n"; - -sub sanitise -{ - $_ = $_[0]; - s/&/&&/g if ($_[1]); - s/([\$@%^*])/\1\1/g; - s/"/\\"/g; - return $_; -} - -sub addalbumentry -{ - my ($playlist, $entry) = @_; - - push(@$playlist, $entry); - - if (defined $artist && $artist eq $entry->{Artist}) { - my $albumdir = albumdir($entry->{file}); - $accept{$albumdir} = 1; - } -} - -sub albumdir -{ - my $file = $_[0]; - - $file =~ s:(/Disk [0-9]+[^/]*)?/[^/]*$::; - return $file -} - -sub albumsort -{ - return ($a->{Disc} <=> $b->{Disc}) if ($a->{Disc} != $b->{Disc}); - return ($a->{Track} <=> $b->{Track}); -} - -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 shellify -{ - my ($str, $quoted) = @_; - $str =~ s/'/'\\''/g; - if ($quoted) { - $str =~ s/\\/\\\\/g; - $str =~ s/"/\\"/g; - } - return "'$str'"; -}