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;
}
}
+sub fmt_duration ($) {
+ my ($n) = @_;
+ 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;
}
defined $r or return {};
my %info = split_fields $r;
decode_track_name $sk, %info;
+ exists $info{sofar} and
+ $info{length} = send_command $sk, "length", $info{track};
return \%info;
}
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;
notify "$TITLE: Now playing", format_now_playing %$info;
}
- while (my $line = readline $sk_log) {
- 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"; }
- notify "$TITLE state", $msg if defined $msg;
- } elsif ($f[1] eq "playing") {
- my %info;
- $info{track} = $f[2];
- $info{submitter} = $f[3] if @f > 3;
- decode_track_name $sk, %info;
- notify "$TITLE: Now playing", format_now_playing %info;
- } elsif ($f[1] eq "scratched") {
- my %info;
- $info{track} = $f[2];
- decode_track_name $sk, %info;
- notify "$TITLE: Scratched by $f[3]", format_now_playing %info;
+ fcntl $sk_log, F_SETFL, (fcntl $sk_log, F_GETFL, 0) | O_NONBLOCK;
+ 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"; }
+ notify "$TITLE state", $msg if defined $msg;
+ } elsif ($f[1] eq "playing") {
+ my %info;
+ $info{track} = $f[2];
+ $info{submitter} = $f[3] if @f > 3;
+ decode_track_name $sk, %info;
+ notify "$TITLE: Now playing", format_now_playing %info;
+ } elsif ($f[1] eq "scratched") {
+ my %info;
+ $info{track} = $f[2];
+ decode_track_name $sk, %info;
+ notify "$TITLE: Scratched by $f[3]", format_now_playing %info;
+ }
+ }
+
+ 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;
}
}
- notify "$TITLE state", "Lost connection";
+ notify "$TITLE state", "Lost connection: $loss";
close $sk;
- close $sk_log;
+ close $sk_log if defined $sk_log;
}
sub watch_and_notify ($) {
my ($now_playing) = @_;
- fork and exit 0;
claim_lock or exit 1;
for (;;) {
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;
$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"; }
};
$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;
};
$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;
- defined locked_by or watch_and_notify 0;
+ unless (defined locked_by) {
+ fork and exit 0;
+ watch_and_notify 0;
+ }
+};
+
+$OP{"next-config"} = sub {
+ (my $dir = $C{config}) =~ s:/[^/]*$::;
+ my (@conf, $curr, $conf, $min);
+
+ if (-l $C{config} && (my $t = readlink $C{config}) =~ /^passwd\.(.*)$/)
+ { $curr = $1; }
+
+ opendir my $dh, +$dir;
+ FILE: while (my $f = readdir $dh)
+ { push @conf, $1 if $f =~ /^passwd\.(.*[^~])$/; }
+
+ for (my $i = 0; $i < @conf; $i++) {
+ $min = $conf[$i] if (!defined $min) || $conf[$i] lt $min;
+ $conf = $conf[$i]
+ if ((!defined $curr) || $curr lt $conf[$i]) &&
+ ((!defined $conf) || $conf[$i] lt $conf);
+ }
+ $conf = $min unless defined $conf;
+
+ try_unlink "$dir/passwd.new";
+ symlink "passwd.$conf", "$dir/passwd.new";
+ rename "$dir/passwd.new", "$dir/passwd";
+ notify "DisOrder configuration", "Switched to `$conf'";
};
###--------------------------------------------------------------------------