Update 2024-02-14 07:51 OpenBSD/amd64-x13
This commit is contained in:
440
.bin/OLD/amused-monitor
Executable file
440
.bin/OLD/amused-monitor
Executable file
@@ -0,0 +1,440 @@
|
||||
#!/usr/bin/env perl
|
||||
#
|
||||
# Copyright (c) 2022 Omar Polo <op@omarpolo.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.12;
|
||||
|
||||
use open ":std", ":encoding(UTF-8)";
|
||||
use utf8;
|
||||
|
||||
use Curses;
|
||||
use POSIX qw(:sys_wait_h setlocale LC_ALL);
|
||||
use Text::CharWidth qw(mbswidth);
|
||||
use IO::Poll qw(POLLIN);
|
||||
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
|
||||
use Getopt::Long qw(:config bundling);
|
||||
use Pod::Usage;
|
||||
|
||||
my $run = 1;
|
||||
|
||||
my $pfile;
|
||||
my $trim = "";
|
||||
|
||||
my $pair_n = 1;
|
||||
|
||||
my @songs;
|
||||
my $current_song;
|
||||
my $playlist_cur;
|
||||
my $playlist_max;
|
||||
my $time_cur;
|
||||
my $time_dur;
|
||||
my $status;
|
||||
my $mode;
|
||||
|
||||
my $last_lines;
|
||||
|
||||
sub round {
|
||||
return int(0.5 + shift);
|
||||
}
|
||||
|
||||
sub max {
|
||||
my ($a, $b) = @_;
|
||||
return $a > $b ? $a : $b;
|
||||
}
|
||||
|
||||
sub excerpt {
|
||||
my $lines = shift;
|
||||
my @tmp;
|
||||
my ($n, $idx, $cur) = (0, 0, -1);
|
||||
|
||||
open (my $fh, "-|", "amused", "show", "-p");
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
s,$trim,,;
|
||||
$tmp[$idx] = $_;
|
||||
|
||||
if (m/^>/) {
|
||||
$cur = $n;
|
||||
$current_song = s/^> //r;
|
||||
}
|
||||
|
||||
$n++;
|
||||
$idx = ++$idx % $lines;
|
||||
|
||||
last if $cur != -1 && $n - $cur > int($lines/2) &&
|
||||
$#tmp == $lines-1;
|
||||
}
|
||||
close($fh);
|
||||
|
||||
return ("Empty playlist.") unless @tmp;
|
||||
|
||||
# reorder the entries
|
||||
my @r;
|
||||
my $len = $#tmp + 1;
|
||||
$idx = $idx % $len;
|
||||
for (1..$len) {
|
||||
push @r, $tmp[$idx];
|
||||
$idx = ++$idx % $len;
|
||||
}
|
||||
return @r;
|
||||
}
|
||||
|
||||
sub playlist_numbers {
|
||||
my ($cur, $tot, $found) = (0, 0, 0);
|
||||
open (my $fh, "-|", "amused", "show", "-p");
|
||||
while (<$fh>) {
|
||||
$tot++;
|
||||
$cur++ unless $found;
|
||||
$found = 1 if m/^>/;
|
||||
}
|
||||
close($fh);
|
||||
return ($cur, $tot);
|
||||
}
|
||||
|
||||
sub status {
|
||||
my ($pos, $dur, $mode);
|
||||
|
||||
open (my $fh, "-|", "amused", "status", "-f",
|
||||
"status,time:raw,mode:oneline");
|
||||
|
||||
<$fh> =~ m/([a-z]+) (.*)/;
|
||||
my ($status, $current_song) = ($1, $2);
|
||||
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
$pos = s/position //r if m/^position /;
|
||||
$dur = s/duration //r if m/^duration /;
|
||||
$mode = $_ if m/^repeat/;
|
||||
}
|
||||
close($fh);
|
||||
return ($status, $current_song, $pos, $dur, $mode);
|
||||
}
|
||||
|
||||
sub showtime {
|
||||
my $seconds = shift;
|
||||
my $str = "";
|
||||
|
||||
if ($seconds > 3600) {
|
||||
my $hours = int($seconds / 3600);
|
||||
$seconds -= $hours * 3600;
|
||||
$str = sprintf("%02d:", $hours);
|
||||
}
|
||||
|
||||
my $minutes = int($seconds / 60);
|
||||
$seconds -= $minutes * 60;
|
||||
$str .= sprintf "%02d:%02d", $minutes, $seconds;
|
||||
return $str;
|
||||
}
|
||||
|
||||
sub center {
|
||||
my ($str, $pstr) = @_;
|
||||
my $width = mbswidth($str);
|
||||
return $str if $width > $COLS;
|
||||
my $pre = round(($COLS - $width) / 2);
|
||||
my $lpad = $pstr x $pre;
|
||||
my $rpad = $pstr x ($COLS - $width - $pre);
|
||||
return ($lpad, $str, $rpad);
|
||||
}
|
||||
|
||||
sub offsets {
|
||||
my ($y, $x, $cur, $max) = @_;
|
||||
my ($pre, $c, $post) = center(" $cur / $max ", '-');
|
||||
addstring $y, $x, "";
|
||||
|
||||
my $p = COLOR_PAIR($pair_n);
|
||||
|
||||
attron $p;
|
||||
addstring $pre;
|
||||
attroff $p;
|
||||
|
||||
addstring $c;
|
||||
|
||||
attron $p;
|
||||
addstring $post;
|
||||
attroff $p;
|
||||
}
|
||||
|
||||
sub progress {
|
||||
my ($y, $x, $pos, $dur) = @_;
|
||||
|
||||
my $pstr = showtime $pos;
|
||||
my $dstr = showtime $dur;
|
||||
|
||||
my $len = $COLS - length($pstr) - length($dstr) - 4;
|
||||
return if $len <= 0 or $dur <= 0;
|
||||
my $filled = round($pos * $len / $dur);
|
||||
|
||||
addstring $y, $x, "$pstr [";
|
||||
addstring "#" x $filled;
|
||||
addstring " " x max($len - $filled, 0);
|
||||
addstring "] $dstr";
|
||||
}
|
||||
|
||||
sub show_status {
|
||||
my ($y, $x, $status) = @_;
|
||||
my ($pre, $c, $post) = center($status, ' ');
|
||||
addstring $y, $x, $pre;
|
||||
addstring $c;
|
||||
addstring $post;
|
||||
}
|
||||
|
||||
sub show_mode {
|
||||
my ($y, $x, $mode) = @_;
|
||||
my ($pre, $c, $post) = center($mode, ' ');
|
||||
addstring $y, $x, $pre;
|
||||
addstring $c;
|
||||
addstring $post;
|
||||
}
|
||||
|
||||
sub render {
|
||||
erase;
|
||||
if ($LINES < 4 || $COLS < 20) {
|
||||
addstring "window too small";
|
||||
refresh;
|
||||
return;
|
||||
}
|
||||
|
||||
my $song_pad = "";
|
||||
my $longest = 0;
|
||||
$longest = max $longest, length($_) foreach @songs;
|
||||
if ($longest < $COLS) {
|
||||
$song_pad = " " x (($COLS - $longest)/2);
|
||||
}
|
||||
|
||||
my $line = 0;
|
||||
map {
|
||||
attron(A_BOLD) if m/^>/;
|
||||
addstring $line++, 0, $song_pad . $_;
|
||||
standend;
|
||||
} @songs;
|
||||
|
||||
offsets $LINES - 4, 0, $playlist_cur, $playlist_max;
|
||||
progress $LINES - 3, 0, $time_cur, $time_dur;
|
||||
show_status $LINES - 2, 0, "$status $current_song";
|
||||
show_mode $LINES - 1, 0, $mode;
|
||||
|
||||
refresh;
|
||||
}
|
||||
|
||||
sub getsongs {
|
||||
$last_lines = $LINES;
|
||||
@songs = excerpt $LINES - 4;
|
||||
}
|
||||
|
||||
sub getnums {
|
||||
($playlist_cur, $playlist_max) = playlist_numbers;
|
||||
}
|
||||
|
||||
sub save {
|
||||
return unless defined $pfile;
|
||||
|
||||
open(my $fh, ">", $pfile);
|
||||
open(my $ph, "-|", "amused", "show", "-p");
|
||||
|
||||
print $fh $_ while (<$ph>);
|
||||
}
|
||||
|
||||
sub hevent {
|
||||
my $fh = shift;
|
||||
my $l = <$fh>;
|
||||
die "monitor quit" unless defined($l);
|
||||
|
||||
$status = "playing" if $l =~ m/^play/;
|
||||
$status = "paused" if $l =~ m/^pause/;
|
||||
$status = "stopped" if $l =~ m/^stop/;
|
||||
|
||||
($time_cur, $time_dur) = ($1, $2) if $l =~ m/^seek (\d+) (\d+)/;
|
||||
|
||||
$mode = $1 if $l =~ m/^mode (.*)/;
|
||||
|
||||
getnums if $l =~ m/load|jump|next|prev/;
|
||||
getsongs if $l =~ m/load|jump|next|prev/;
|
||||
}
|
||||
|
||||
sub hinput {
|
||||
my ($ch, $key) = getchar;
|
||||
if (defined $key) {
|
||||
if ($key == KEY_BACKSPACE) {
|
||||
system "amused", "seek", "0";
|
||||
}
|
||||
} elsif (defined $ch) {
|
||||
if ($ch eq " ") {
|
||||
system "amused", "toggle";
|
||||
} elsif ($ch eq "<" or $ch eq "p") {
|
||||
system "amused", "prev";
|
||||
} elsif ($ch eq ">" or $ch eq "n") {
|
||||
system "amused", "next";
|
||||
} elsif ($ch eq ",") {
|
||||
system "amused", "seek", "-5";
|
||||
} elsif ($ch eq ".") {
|
||||
system "amused", "seek", "+5";
|
||||
} elsif ($ch eq "S") {
|
||||
system "amused show | sort -u | amused load";
|
||||
} elsif ($ch eq "R") {
|
||||
system "amused show | sort -R | amused load";
|
||||
} elsif ($ch eq "s") {
|
||||
save;
|
||||
} elsif ($ch eq "q") {
|
||||
$run = 0;
|
||||
} elsif ($ch eq "\cH") {
|
||||
system "amused", "seek", "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetOptions(
|
||||
"p:s" => \$pfile,
|
||||
"t:s" => \$trim,
|
||||
) or pod2usage(1);
|
||||
|
||||
my $mpid = open(my $monitor, "-|", "amused", "monitor")
|
||||
or die "can't spawn amused monitor";
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
initscr;
|
||||
start_color;
|
||||
use_default_colors;
|
||||
init_pair $pair_n, 250, -1;
|
||||
|
||||
timeout 1000;
|
||||
scrollok 0;
|
||||
curs_set 0;
|
||||
keypad 1;
|
||||
|
||||
my $poll = IO::Poll->new();
|
||||
$poll->mask(\*STDIN => POLLIN);
|
||||
$poll->mask($monitor => POLLIN);
|
||||
|
||||
if (`uname` =~ "OpenBSD") {
|
||||
use OpenBSD::Pledge;
|
||||
use OpenBSD::Unveil;
|
||||
|
||||
my $prog = `which amused`;
|
||||
chomp $prog;
|
||||
|
||||
unveil($prog, 'rx') or die "unveil $prog: $!";
|
||||
if (defined($pfile)) {
|
||||
unveil($pfile, 'wc') or die "unveil $pfile: $!";
|
||||
pledge qw(stdio wpath cpath tty proc exec) or die "pledge: $!";
|
||||
} else {
|
||||
pledge qw(stdio tty proc exec) or die "pledge: $!";
|
||||
}
|
||||
}
|
||||
|
||||
getsongs;
|
||||
getnums;
|
||||
($status, $current_song, $time_cur, $time_dur, $mode) = status;
|
||||
render;
|
||||
|
||||
while ($run) {
|
||||
$poll->poll();
|
||||
hinput if $poll->events(\*STDIN) & POLLIN;
|
||||
hevent $monitor if $poll->events($monitor) & POLLIN;
|
||||
|
||||
getsongs if $LINES != $last_lines;
|
||||
|
||||
render;
|
||||
}
|
||||
|
||||
endwin;
|
||||
save;
|
||||
|
||||
kill 'INT', $mpid;
|
||||
wait;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
amused-monitor - curses interface for amused(1)
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<amused-monitor> [B<-p> I<playlist>] [B<-t> I<string>]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
amused-monitor is a simple curses interface for amused(1).
|
||||
|
||||
The following options are available:
|
||||
|
||||
=over 12
|
||||
|
||||
=item B<-p> I<playlist>
|
||||
|
||||
Save the current playling queue to the file I<playlist> upon exit or
|
||||
I<s> key.
|
||||
|
||||
=item B<-t> I<string>
|
||||
|
||||
Trim out the given I<string> from every song in the playlist view.
|
||||
|
||||
=back
|
||||
|
||||
The following key-bindings are available:
|
||||
|
||||
=over 8
|
||||
|
||||
=item backspace or C-h
|
||||
|
||||
Seek back to the beginning of the track.
|
||||
|
||||
=item space
|
||||
|
||||
Toggle play/pause.
|
||||
|
||||
=item < or p
|
||||
|
||||
Play previous song.
|
||||
|
||||
=item > or n
|
||||
|
||||
Play next song.
|
||||
|
||||
=item ,
|
||||
|
||||
Seek backward by five seconds.
|
||||
|
||||
=item .
|
||||
|
||||
Seek forward by five seconds.
|
||||
|
||||
=item R
|
||||
|
||||
Randomize the playlist.
|
||||
|
||||
=item S
|
||||
|
||||
Sort the playlist.
|
||||
|
||||
=item s
|
||||
|
||||
Save the status to the file given with the B<-p> flag.
|
||||
|
||||
=item q
|
||||
|
||||
Quit.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
amused(1)
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user