3 # Copyright (c) 2022 Omar Polo <op@omarpolo.com>
5 # Permission to use, copy, modify, and distribute this software for any
6 # purpose with or without fee is hereby granted, provided that the above
7 # copyright notice and this permission notice appear in all copies.
9 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 use POSIX ":sys_wait_h";
23 use Text::CharWidth qw(mbswidth);
24 use IO::Poll qw(POLLIN);
25 use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
26 use Getopt::Long qw(:config bundling);
47 return int(0.5 + shift);
52 return $a > $b ? $a : $b;
58 my ($n, $idx, $cur) = (0, 0, -1);
60 open (my $fh, "-|", "amused", "show", "-p");
67 $idx = ++$idx % $lines;
69 last if $cur != -1 && $n - $cur > int($lines/2) &&
74 return ("Empty playlist.") unless @tmp;
87 sub playlist_numbers {
88 my ($cur, $tot, $found) = (0, 0, 0);
89 open (my $fh, "-|", "amused", "show", "-p");
100 my ($pos, $dur, $mode);
102 open (my $fh, "-|", "amused", "status", "-f",
103 "status,time:raw,mode:oneline");
108 $pos = s/position //r if m/^position /;
109 $dur = s/duration //r if m/^duration /;
110 $mode = $_ if m/^repeat/;
113 return ($status, $pos, $dur, $mode);
120 if ($seconds > 3600) {
121 my $hours = int($seconds / 3600);
122 $seconds -= $hours * 3600;
123 $str = sprintf("%02:", $hours);
126 my $minutes = int($seconds / 60);
127 $seconds -= $minutes * 60;
128 $str .= sprintf "%02d:%02d", $minutes, $seconds;
133 my ($str, $pstr) = @_;
134 my $width = mbswidth($str);
135 return $str if $width > $COLS;
136 my $pre = ($COLS - $width) / 2;
137 my $lpad = $pstr x $pre;
138 my $rpad = $pstr x ($COLS - $width - $pre + 1);
139 return ($lpad, $str, $rpad);
143 my ($y, $x, $cur, $max) = @_;
144 my ($pre, $c, $post) = center(" $cur / $max ", '-');
145 addstring $y, $x, "";
147 my $p = COLOR_PAIR($pair_n);
161 my ($y, $x, $pos, $dur) = @_;
163 my $pstr = showtime $pos;
164 my $dstr = showtime $dur;
166 my $len = $COLS - length($pstr) - length($dstr) - 4;
167 return if $len <= 0 or $dur <= 0;
168 my $filled = round($pos * $len / $dur);
170 addstring $y, $x, "$pstr [";
171 addstring "#" x $filled;
172 addstring " " x max($len - $filled, 0);
177 my ($y, $x, $status) = @_;
178 my ($pre, $c, $post) = center($status, ' ');
179 addstring $y, $x, $pre;
185 my ($y, $x, $mode) = @_;
186 my ($pre, $c, $post) = center($mode, ' ');
187 addstring $y, $x, $pre;
194 if ($LINES < 4 || $COLS < 20) {
195 addstring "window too small";
202 $longest = max $longest, length($_) foreach @songs;
203 if ($longest < $COLS) {
204 $song_pad = " " x (($COLS - $longest)/2);
209 attron(A_BOLD) if m/^>/;
210 addstring $line++, 0, $song_pad . $_;
214 offsets $LINES - 4, 0, $playlist_cur, $playlist_max;
215 progress $LINES - 3, 0, $time_cur, $time_dur;
216 show_status $LINES - 2, 0, $status;
217 show_mode $LINES - 1, 0, $mode;
223 $last_lines = $LINES;
224 @songs = excerpt $LINES - 4;
228 ($playlist_cur, $playlist_max) = playlist_numbers;
232 ($status, $time_cur, $time_dur, $mode) = status;
236 return unless defined $pfile;
238 open(my $fh, ">", $pfile);
239 open(my $ph, "-|", "amused", "show", "-p");
241 print $fh $_ while (<$ph>);
247 die "monitor quit" unless defined($l);
249 getnums if $l =~ m/load|jump|next|prev/;
250 getsongs if $l =~ m/load|jump|next|prev/;
254 my ($ch, $key) = getchar;
257 system "amused", "toggle";
258 } elsif ($ch eq "<" or $ch eq "p") {
259 system "amused", "prev";
260 } elsif ($ch eq ">" or $ch eq "n") {
261 system "amused", "next";
262 } elsif ($ch eq ",") {
263 system "amused", "seek", "-5";
264 } elsif ($ch eq ".") {
265 system "amused", "seek", "+5";
266 } elsif ($ch eq "S") {
267 system "amused show | sort -u | amused load";
268 } elsif ($ch eq "R") {
269 system "amused show | sort -R | amused load";
270 } elsif ($ch eq "s") {
272 } elsif ($ch eq "q") {
275 } elsif (defined $key) {
285 my $mpid = open(my $monitor, "-|", "amused", "monitor")
286 or die "can't spawn amused monitor";
291 init_pair $pair_n, 250, -1;
297 my $poll = IO::Poll->new();
298 $poll->mask(\*STDIN => POLLIN);
299 $poll->mask($monitor => POLLIN);
302 my $tbefore = clock_gettime(CLOCK_MONOTONIC);
311 my $now = clock_gettime(CLOCK_MONOTONIC);
312 my $elapsed = $now - $tbefore;
315 $time_cur += round($elapsed)
316 if $status =~ m/^playing/;
319 getstatus unless $tick++ % 8;
321 hinput if $poll->events(\*STDIN) & POLLIN;
322 hevent $monitor if $poll->events($monitor) & POLLIN;
324 getsongs if $LINES != $last_lines;
341 amused-monitor - curses interface for amused(1)
345 B<amused-monitor> [B<-p> I<playlist>] [B<-t> I<string>]
349 amused-monitor is a simple curses interface for amused(1).
351 The following options are available:
355 =item B<-p> I<playlist>
357 Save the current playling queue to the file I<playlist> upon exit or
360 =item B<-t> I<string>
362 Trim out the given I<string> from every song in the playlist view.
366 The following key-bindings are available:
384 Seek backward by five seconds.
388 Seek forward by five seconds.
392 Randomize the playlist.
400 Save the status to the file given with the B<-p> flag.