dot/emacs: Apply Viper settings as the defaults.
[profile] / bin / disorder-notify
index 3c57073..2b0d5e8 100755 (executable)
@@ -5,6 +5,8 @@ use strict;
 
 use DisOrder;
 use File::FcntlLock;
+use Getopt::Long qw{:config gnu_compat bundling
+                   require_order no_getopt_compat};
 use POSIX qw{:errno_h :fcntl_h};
 
 ###--------------------------------------------------------------------------
@@ -14,6 +16,7 @@ my %C = (config => "$ENV{HOME}/.disorder/passwd",
         lockdir => "$ENV{HOME}/.disorder/",
         mixer => "Master,0");
 
+(my $PROG = $0) =~ s:^.*/::;
 my $TITLE = "DisOrder";
 my $VARIANT = "default";
 if (-l $C{config} && (my $t = readlink $C{config}) =~ /^passwd\.(.*)$/)
@@ -124,8 +127,16 @@ sub get_state0 ($) {
   return \%st;
 }
 
+my $CONF = undef;
+
+sub configured_connection (;$) {
+  my ($quietp) = @_;
+  $CONF //= load_config $C{config};
+  return connect_to_server %$CONF, $quietp // 0;
+}
+
 sub get_state () {
-  my $sk = connect_to_server $C{config};
+  my $sk = configured_connection;
   send_command0 $sk, "log";
   my $st = get_state0 $sk;
   close $sk;
@@ -147,18 +158,6 @@ sub fmt_duration ($) {
   return sprintf "%d:%02d", int $n/60, $n%60;
 }
 
-sub format_now_playing (\%) {
-  my ($info) = @_;
-  exists $info->{track} or return "Nothing.";
-  my $r = "$info->{artist}: ‘$info->{title}’";
-  $r .= ", from ‘$info->{album}’" if $info->{album};
-  exists $info->{sofar} && exists $info->{length} and
-    $r .= sprintf " (%s/%s)",
-      fmt_duration $info->{sofar}, fmt_duration $info->{length};
-  $r .= "\n(chosen by $info->{submitter})" if exists $info->{submitter};
-  return $r;
-}
-
 sub get_now_playing ($) {
   my ($sk) = @_;
   my $r = send_command $sk, "playing";
@@ -170,11 +169,28 @@ sub get_now_playing ($) {
   return \%info;
 }
 
+sub format_now_playing (;\%) {
+  my ($info) = @_;
+  unless (defined $info) {
+    my $sk = configured_connection;
+    $info = get_now_playing $sk;
+    close $sk;
+  }
+  exists $info->{track} or return "Nothing.";
+  my $r = "$info->{artist}: ‘$info->{title}’";
+  $r .= ", from ‘$info->{album}’" if $info->{album};
+  exists $info->{sofar} && exists $info->{length} and
+    $r .= sprintf " (%s/%s)",
+      fmt_duration $info->{sofar}, fmt_duration $info->{length};
+  $r .= "\n(chosen by $info->{submitter})" if exists $info->{submitter};
+  return $r;
+}
+
 sub watch_and_notify0 ($) {
   my ($now_playing) = @_;
 
-  my $sk = connect_to_server $C{config}, 1;
-  my $sk_log = connect_to_server $C{config}, 1;
+  my $sk = configured_connection 1;
+  my $sk_log = configured_connection 1;
 
   send_command0 $sk_log, "log";
   my $st = get_state0 $sk_log;
@@ -191,18 +207,25 @@ sub watch_and_notify0 ($) {
   my $buffer = "";
   my @lines = ();
   my $rdin = ""; vec($rdin, (fileno $sk_log), 1) = 1;
+  my $loss;
 
   WATCH: for (;;) {
     for my $line (@lines) {
       my @f = split_fields $line;
       if ($f[1] eq "state") {
        my $msg = undef;
-       if ($f[2] eq "disable_random") { $msg = "Random play disabled"; }
-       elsif ($f[2] eq "enable_random") { $msg = "Random play enabled"; }
-       elsif ($f[2] eq "disable_play") { $msg = "Playing disabled"; }
-       elsif ($f[2] eq "enable_play") { $msg = "Playing enabled"; }
-       elsif ($f[2] eq "pause") { $msg = "Paused"; }
-       elsif ($f[2] eq "resume") { $msg = "Playing"; }
+       if ($f[2] eq "disable_random")
+         { $st->{random} = 0; $msg = "Random play disabled"; }
+       elsif ($f[2] eq "enable_random")
+         { $st->{random} = 1; $msg = "Random play enabled"; }
+       elsif ($f[2] eq "disable_play")
+         { $st->{play} = 0; $msg = "Playing disabled"; }
+       elsif ($f[2] eq "enable_play")
+         { $st->{play} = 1; $msg = "Playing enabled"; }
+       elsif ($f[2] eq "pause")
+         { $st->{pause} = 1; $msg = "Paused"; }
+       elsif ($f[2] eq "resume")
+         { $st->{pause} = 0; $msg = "Playing"; }
        notify "$TITLE state", $msg if defined $msg;
       } elsif ($f[1] eq "playing") {
        my %info;
@@ -215,25 +238,33 @@ sub watch_and_notify0 ($) {
        $info{track} = $f[2];
        decode_track_name $sk, %info;
        notify "$TITLE: Scratched by $f[3]", format_now_playing %info;
+      } elsif ($f[1] eq "completed" && !$st->{play}) {
+       notify "$TITLE state", "Stopped";
       }
     }
 
-    last WATCH unless $sk_log;
-    select my $rdout = $rdin, undef, undef, undef;
-    READ: for (;;) {
-      my ($b, $n);
-      eval { $n = sysread $sk_log, $b, 4096; };
-      if ($@ && $@->errno == EAGAIN) { last READ; }
-      elsif ($@) { die $@; }
-      elsif (!$n) { close $sk_log; $sk_log = undef; }
-      else { $buffer .= $b; }
-    }
+    if (!$sk_log) { $loss = "EOF from server"; last WATCH; }
+    my $nfd = select my $rdout = $rdin, undef, undef, 60;
+    if (!$nfd) {
+      eval { print $sk_log "."; flush $sk_log; };
+      if ($@) { $loss = "error from write: " . $@->errno; last WATCH; }
+      @lines = ();
+    } else {
+      READ: for (;;) {
+       my ($b, $n);
+       eval { $n = sysread $sk_log, $b, 4096; };
+       if ($@ && $@->errno == EAGAIN) { last READ; }
+       elsif ($@) { $loss = "error from read: " . $@->errno; last WATCH; }
+       elsif (!$n) { close $sk_log; $sk_log = undef; last READ; }
+       else { $buffer .= $b; }
+      }
 
-    @lines = split /\n/, $buffer, -1;
-    $buffer = pop @lines;
+      @lines = split /\n/, $buffer, -1;
+      $buffer = pop @lines;
+    }
   }
 
-  notify "$TITLE state", "Lost connection";
+  notify "$TITLE state", "Lost connection: $loss";
 
   close $sk;
   close $sk_log if defined $sk_log;
@@ -262,14 +293,14 @@ $OP{"volume-down"} =
   sub { run_discard_output "amixer", "sset", $C{mixer}, "5\%-"; };
 
 $OP{"scratch"} = sub {
-  my $sk = connect_to_server $C{config};
+  my $sk = configured_connection;
   send_command $sk, "scratch";
   close $sk;
 };
 
 $OP{"enable/disable"} = sub {
   my $st = get_state;
-  my $sk = connect_to_server $C{config};
+  my $sk = configured_connection;
   if ($st->{play}) { send_command $sk, "disable"; }
   else { send_command $sk, "enable"; }
   close $sk;
@@ -277,7 +308,7 @@ $OP{"enable/disable"} = sub {
 
 $OP{"play/pause"} = sub {
   my $st = get_state;
-  my $sk = connect_to_server $C{config};
+  my $sk = configured_connection;
   if (!$st->{play}) {
     send_command $sk, "enable";
     if ($st->{pause}) { send_command $sk, "resume"; }
@@ -297,7 +328,7 @@ $OP{"watch"} = sub {
 };
 
 $OP{"now-playing"} = sub {
-  my $sk = connect_to_server $C{config};
+  my $sk = configured_connection;
   my $info = get_now_playing $sk;
   close $sk;
   print format_now_playing %$info;
@@ -305,7 +336,7 @@ $OP{"now-playing"} = sub {
 };
 
 $OP{"notify-now-playing"} = sub {
-  my $sk = connect_to_server $C{config};
+  my $sk = configured_connection;
   my $info = get_now_playing $sk;
   close $sk;
   notify "$TITLE: Now playing", format_now_playing %$info;
@@ -343,7 +374,39 @@ $OP{"next-config"} = sub {
 ###--------------------------------------------------------------------------
 ### Main program.
 
-if (@ARGV != 1) { print STDERR "usage: $0 OP\n"; exit 2; }
+sub usage (\*) {
+  my ($fh) = @_;
+  print $fh "usage: $PROG [-u CONFIG] COMMAND\n";
+}
+
+sub help () {
+  usage *STDOUT;
+  print <<EOF;
+
+Command-line options:
+  -h, --help           Show this help text
+  -u, --user-config    Set user configuration file
+
+Commands:
+  volume-up
+  volume-down
+  scratch
+  enable/disable
+  play/pause
+  watch
+  now-playing
+  notify-now-playing
+  next-config
+EOF
+}
+
+my $bad = 0;
+GetOptions
+  "h|help" => sub { help; exit 0; },
+  "u|user-config=s" => \$C{config}
+    or $bad = 1;
+@ARGV == 1 or $bad = 1;
+if ($bad) { usage *STDERR; exit 2; }
 my $op = $ARGV[0];
 if (!exists $OP{$op}) { print STDERR "$0: unknown op `$op'\n"; exit 2; }
 $OP{$op}();