Openbox:Pipemenus:Audacious control alternative

From Openbox

(Difference between revisions)
Jump to: navigation, search
m (bug fix)
m
Line 408: Line 408:
 
   
 
   
 
  sub import_pls {
 
  sub import_pls {
    system("audtool --playlist-clear");
 
 
     chomp (my $file = `zenity --file-selection --title="Import playlist" --filename=/media/Music/ --file-filter='*.m3u' --file-filter='*'`);
 
     chomp (my $file = `zenity --file-selection --title="Import playlist" --filename=/media/Music/ --file-filter='*.m3u' --file-filter='*'`);
 
     exit 1 unless $file;
 
     exit 1 unless $file;
 
     my $dir = dirname $file;
 
     my $dir = dirname $file;
 
     open PLS, "$file" or ( system("zenity --error --text=\"Can't read $file\!\"") and exit 1 );
 
     open PLS, "$file" or ( system("zenity --error --text=\"Can't read $file\!\"") and exit 1 );
 +
    system("audtool --playlist-clear");
 
     while ( <PLS> ) {
 
     while ( <PLS> ) {
 
  chomp;
 
  chomp;

Revision as of 16:04, 1 October 2014

The idea for this menu came from this other audacious menu, but this is a slightly expanded version. The "Show/hide main window" feature depends on wmctrl - this is needed to detect if the window is shown or not and set the item label/action accordingly. If you do not have wmctrl abd have other means of getting the information, adjust code accordingly or remove the item. The "add/remove files" features depend on zenity to create the file selection and text entry boxes - again, if you don't have zenity, adjust code (the appending of files to the end of playlist can be done via audtool --filebrowser-show on).

This menu consists of two files - the actual menu (audmenu) and the script (audctrl) that controls adding and removing songs. Both need to be executable. The script needs to be in your PATH (or you would need to specify the full path to it everywhere in the code of audmenu.

The audctrl script also provides a more extended "previous song" feature - audtool does not jump from first to last song even when repeat is on (it only allows jumping from last to first when selecting "next song").


audmenu:

#!/usr/bin/perl
# A pipe menu for openbox to control Audacious.
# Depends on wmctrl, zenity and an additional script audctrl.
# All the functionality of audctrl can be incorporated into this script (as was done with the playlist submenu),
# but this way, it can be used independently of this menu in a complementary way to audtool.
# audctrl should be in your PATH
use Cwd 'abs_path';

sub print_plst;
sub say { print @_, "\n"; }

# path to this script needed because it calls itself to create the playlist submenu
my $path = abs_path $0;

chomp (our $pid = `pidof audacious`);
our $status;
our $curr_song;
our $curr_song_lgth;
our $curr_song_out;
our $curr_song_pos;
our $plst_lgth;
our $title;

# if audacious is running, get info on playlist
if ( $pid ) {
    chomp ($status = `audtool --playback-status`);
    chomp ($curr_song = `audtool --current-song`);
    $curr_song = "" if $curr_song eq "No song playing.";
    chomp ($curr_song_lgth = `audtool --current-song-length`) if $curr_song;
    chomp ($curr_song_out = `audtool --current-song-output-length`) if $curr_song;
    chomp ($curr_song_pos = `audtool --playlist-position`) if $curr_song;
    chomp ($plst_lgth = `audtool --playlist-length`);
} else {
    $status = 'off';
}

# print playlist submenu and exit if arg 1 is pls
print_plst if "$ARGV[0]" eq "pls";

# otherwise, start printing the main menu
say "<openbox_pipe_menu>";

# if audacious is not running, print "start" option and exit; else determine status and set playlist menu label
if ( $status eq "off" ) {
    say "  <item label=\"Start audacious\">";
    say "    <action name=\"Execute\">";
    say "      <execute>";
    say "        audacious";
    say "      </execute>";
    say "    </action>";
    say "  </item>";
    say "</openbox_pipe_menu>";
    exit 0;
}
# if audacious is running but no song is playing, print information about the current song in the playlist
if ( $status eq "stopped" ) {
    $title = "Stopped: $curr_song_pos. $curr_song ($curr_song_lgth)" if $curr_song;
    $title = "Stopped" unless $curr_song;
} elsif ( $status eq "paused") {
    $title = "Paused: $curr_song_pos. $curr_song ($curr_song_out / $curr_song_lgth)";
} else {
    $title = "Playing: $curr_song_pos. $curr_song ($curr_song_out / $curr_song_lgth)";
}

# unless playlist is empty, print it as menu, otherwise - just put a separator with the status
if ( $plst_lgth == 0 ) {
    say "<separator label=\"$title\" />";
} else {
    say "<menu id=\"pls\" label=\"$title\" execute=\"$path pls\" />";
    say "<separator />";
}

# play/pause song
if ( $status eq "playing") {
    say "  <item label=\"Pause\">";
} else {
    say "  <item label=\"Play\">";
}
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playback-playpause";
say "      </execute>";
say "    </action>";
say "  </item>";

# stop playback
say "  <item label=\"Stop\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playback-stop";
say "      </execute>";
say "    </action>";
say "  </item>";

# play next/previous song; 'audctrl prev' accounts for the case where repeat is on and current song is number 1
say "  <item label=\"Next\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playlist-advance";
say "      </execute>";
say "    </action>";
say "  </item>";
say "  <item label=\"Previous\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl prev";
say "      </execute>";
say "    </action>";
say "  </item>";
say "<separator />";

# toggle "stop after current song"
if ( `audtool --playlist-stop-after-status` eq "off\n" ) {
    say "  <item label=\"Stop after current song\">";
} else {
    say "  <item label=\"Continue after current song\">";
}
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playlist-stop-after-toggle";
say "      </execute>";
say "    </action>";
say "  </item>";

# toggle "repeat playlist"
if ( `audtool --playlist-repeat-status` eq "off\n" ) {
    say "  <item label=\"Repeat playlist\">";
} else {
    say "  <item label=\"Do not repeat playlist\">";
}
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playlist-repeat-toggle";
say "      </execute>";
say "    </action>";
say "  </item>";

# toggle "shuffle playlist"
if ( `audtool --playlist-shuffle-status` eq "off\n" ) {
    say "  <item label=\"Shuffle playlist\">";
} else {
    say "  <item label=\"Do not shuffle playlist\">";
}
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playlist-shuffle-toggle";
say "      </execute>";
say "    </action>";
say "  </item>";
say "<separator />";

# import playlist
say "  <item label=\"Import playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl import";
say "      </execute>";
say "    </action>";
say "  </item>";

# export playlist
say "  <item label=\"Export playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl export";
say "      </execute>";
say "    </action>";
say "  </item>";

# add files to end of playlist
say "  <item label=\"Append files to playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl add";
say "      </execute>";
say "    </action>";
say "  </item>";

# add directories to end of playlist
say "  <item label=\"Append directories to playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl add_dir";
say "      </execute>";
say "    </action>";
say "  </item>";

# add files to playlist at position...
say "  <item label=\"Add files to playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl add_pos";
say "      </execute>";
say "    </action>";
say "  </item>";

# add directories to playlist at position...
say "  <item label=\"Add directories to playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl add_dir_pos";
say "      </execute>";
say "    </action>";
say "  </item>";

# remove songs from playlist
say "  <item label=\"Remove songs from playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl del";
say "      </execute>";
say "    </action>";
say "  </item>";

# clear playlist
say "  <item label=\"Clear playlist\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --playlist-clear";
say "      </execute>";
say "    </action>";
say "  </item>";

# clear all but the current song
say "  <item label=\"Clear all but the current song\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audctrl clear";
say "      </execute>";
say "    </action>";
say "  </item>";
say "<separator />";

# show hide main window
if ( `wmctrl -lp | tr -s ' ' | cut -d' ' -f3` =~ /^$pid$/m ) {
    say "  <item label=\"Hide main window\">";
    say "    <action name=\"Execute\">";
    say "      <execute>";
    say "        audtool --mainwin-show off";
} else {
    say "  <item label=\"Show main window\">";
    say "    <action name=\"Execute\">";
    say "      <execute>";
    say "        audtool --mainwin-show on";
}
say "      </execute>";
say "    </action>";
say "  </item>";

# shutdown audacious
say "  <item label=\"Shutdown Audacious\">";
say "    <action name=\"Execute\">";
say "      <execute>";
say "        audtool --shutdown";
say "      </execute>";
say "    </action>";
say "  </item>";

say "</openbox_pipe_menu>";

##################################################################################################
########################################### SUBROUTINES ##########################################
##################################################################################################

# playlist submenu (called by passing a 'pls' argument to this script)
sub print_plst {
    say "<openbox_pipe_menu>";
    for my $song ( 1 .. $plst_lgth ) {
	chomp (my $title = `audtool --playlist-song $song`);
	chomp (my $lgth = `audtool --playlist-song-length $song`);
	
# replace some special characters by their html codes	
	$title =~ s/&/&/g;
	$title =~ s/"/"/g;
	$title =~ s/\$/$/g;
	$title =~ s/</</g;
	$title =~ s/=/=/g;
	$title =~ s/>/>/g;
	$title =~ s/\\/\/g;
# replace the underscore with a double underscore in the label to prevent openbox from interpreting it as a keyboard accelerator
	$title =~ s/_/__/g;
	
	say "  <item label=\"$song. $title ($lgth)\">";
	say "    <action name=\"Execute\">";
	say "      <execute>";
	say "        audtool --playlist-jump $song";
	say "      </execute>";
	say "    </action>";
	say "  </item>";
    }
    say "</openbox_pipe_menu>";
    exit 0;
}

audctrl:

#!/usr/bin/perl
# Additional functions to control Audacious. Depends on zenity.

use File::Basename;
use File::Find;

use subs clear_others, del, import_pls, export_pls, prev;
sub sort_dirs { return sort @_; }
sub get_files {
    return if $_ eq '.' or not -f $_;
    $_ = $File::Find::dir . "/" . $_;
    push @files, $_;
}
sub add ($$);

clear_others if "$ARGV[0]" eq "clear";
del if "$ARGV[0]" eq "del";
add 0,0 if "$ARGV[0]" eq "add";
add 0,1 if "$ARGV[0]" eq "add_pos";
add 1,0 if "$ARGV[0]" eq "add_dir";
add 1,1 if "$ARGV[0]" eq "add_dir_pos";
import_pls if "$ARGV[0]" eq "import";
export_pls if "$ARGV[0]" eq "export";
prev if "$ARGV[0]" eq "prev";
exit 1;

##################################################################################################
########################################### SUBROUTINES ##########################################
##################################################################################################

# Remove all but the current song
sub clear_others {
    chomp (my $plst_lgth = `audtool --playlist-length`);
    if ( $plst_lgth > 1 ) {
	my $del = 1;
	chomp (my $curr_song = `audtool --playlist-position`);
	PLS: for my $song ( 1 .. $plst_lgth ) {
	    if ( $song == $curr_song ) {
		$del = 2;
		next PLS;
	    }
	    system("audtool --playlist-delete $del");
	}
    }
    exit 0;
}

# Remove songs at positions (needs zenity)
sub del {
    chomp (my $sel = `zenity --entry --text="Remove songs at positions (coma separated, or a range in the form x..y):"`);
    $sel =~ s/\ //g;
    exit 1 unless $sel;
    my @pos;
    @pos = ( $1 .. $2 ) if $sel =~ /^(\d+)\.\.(\d+)$/;
    @pos = split(',' , $sel) unless @pos;
    my $iter = 0;
    for (sort { $a <=> $b } @pos) {
	my $del = $_ - $iter;
	system("audtool --playlist-delete $del");
	if ( $? ) {
	    system("zenity --error --text=\"No such song number\!\"");
	    exit 1;
	}
	$iter++;
    }
    exit 0;
}

# Add songs to specified positions or the end of the playlist
sub add ($$) {
    my $dirmode = shift;
    my $insert = shift;
    my $sel;
    local @files;
    if ( $dirmode ) {
	chomp ($sel = `zenity --file-selection --title="Add files" --filename=/media/Music/  --separator='|' --multiple --directory`);
	exit 1 unless $sel;
	find ( { preprocess => \&sort_dirs, wanted => \&get_files }, split(/\|/,"$sel") );
    } else {
	chomp ($sel = `zenity --file-selection --title="Add files" --filename=/media/Music/  --separator='|' --multiple --file-filter='*.mp3' --file-filter='*.flac' --file-filter='*.wav' --file-filter='*.ogg' --file-filter='*'`);
	exit 1 unless $sel;
	@files = split /\|/ , "$sel";
    }
    my $pos = -1;
    my $cmd;
    if ( $insert ) {
	chomp ($pos = `zenity --entry --text='Insert files at position:'`);
	$pos =~ s/\ //g;
    }
    exit 1 unless $pos;
    chomp (my $plst_lgth = `audtool --playlist-length`);
    if ( $insert && ( $pos < 1 || $pos > $plst_lgth ) ) {
	system("zenity --error --text=\"Invalid playlist position\!\"");
	exit 2;
    }
    if ( $insert ) {
	system("audtool --playlist-insurl \"$_\" $pos") for reverse @files;
    } else {
	system("audtool --playlist-addurl \"$_\"") for @files;
    }
    exit 0;
}

sub import_pls {
    chomp (my $file = `zenity --file-selection --title="Import playlist" --filename=/media/Music/ --file-filter='*.m3u' --file-filter='*'`);
    exit 1 unless $file;
    my $dir = dirname $file;
    open PLS, "$file" or ( system("zenity --error --text=\"Can't read $file\!\"") and exit 1 );
    system("audtool --playlist-clear");
    while ( <PLS> ) {
	chomp;
	$_ = $dir . '/' . $_ unless $_ =~ /\//;
	system("audtool --playlist-addurl \"$_\"");
    }
    close PLS;
    exit 0;
}

sub export_pls {
    chomp (my $file = `zenity --file-selection --title="Export playlist" --filename=/media/Music/ --file-filter='*.m3u' --file-filter='*' --save --confirm-overwrite`);
    exit 1 unless $file;
    open PLS, ">$file" or ( system("zenity --error --text=\"Can't write to $file\!\"") and exit 1 );
    chomp (my $plst_lgth = `audtool --playlist-length`);
    for my $song ( 1 .. $plst_lgth ) {
	my $file = `audtool --playlist-song-filename $song`;
	print PLS $file;
    }
    close PLS;
    exit 0;
}

# Jump to previous song - accounts for the case when repeat is on and the current song is number 1
sub prev {
    if ( `audtool --playlist-repeat-status` eq "on\n" && `audtool --playlist-position` == "1\n" ) {
	chomp (my $plst_lgth = `audtool --playlist-length`);
	system("audtool --playlist-jump $plst_lgth");
	exit 0;
    }
    system("audtool --playlist-reverse");
    exit 0;
}
Personal tools