8 use POSIX qw{:errno_h :fcntl_h};
10 ###--------------------------------------------------------------------------
13 my %C = (config => "$ENV{HOME}/.disorder/passwd",
14 lockdir => "$ENV{HOME}/.disorder/",
17 my $TITLE = "DisOrder";
18 my $VARIANT = "default";
19 if (-l $C{config} && (my $t = readlink $C{config}) =~ /^passwd\.(.*)$/)
20 { $VARIANT = $1; $TITLE .= " ($1)"; }
22 ###--------------------------------------------------------------------------
25 sub run_discard_output (@) {
28 open STDOUT, ">/dev/null" or die "open /dev/null: $!";
34 if ($? >= 256) { $st = sprintf "rc = %d", $? >> 8; }
35 else { $st = sprintf "signal %d", $?; }
36 die "$_[0] failed ($st)";
41 my ($head, $body) = @_;
43 $body =~ s:\&:&:g;
47 ##print "****************\n$head\n\n$body\n"; return;
49 run_discard_output "notify-send",
50 "-c", "DisOrder", "-i", "audio-volume-high", "-t", "5000",
57 die $@ if $@ and $@->errno != ENOENT;
60 ###--------------------------------------------------------------------------
63 my $LKFILE = "$C{lockdir}/disorder-notify-$VARIANT.lock";
68 ## Try to open the lock file. If it's not there, then obviously it's not
71 eval { open $fh, "<", $LKFILE; };
73 return undef if $@->errno == ENOENT;
77 ## Take out a non-exclusive lock on the lock file.
78 my $lk = new File::FcntlLock;
79 $lk->l_type(F_RDLCK); $lk->l_whence(SEEK_SET);
80 $lk->l_start(0); $lk->l_len(0);
81 if ($lk->lock($fh, F_SETLK)) { close $fh; return undef; }
83 ## Read the pid of the current lock-holder.
84 chomp (my $pid = (readline $fh) // "<unknown>");
90 sysopen my $fh, $LKFILE, O_CREAT | O_WRONLY;
92 my $lk = new File::FcntlLock;
93 $lk->l_type(F_WRLCK); $lk->l_whence(SEEK_SET);
94 $lk->l_start(0); $lk->l_len(0);
95 if (!$lk->lock($fh, F_SETLK)) {
96 return undef if $! == EAGAIN;
97 die "failed to lock `$LKFILE': $!";
107 ###--------------------------------------------------------------------------
108 ### DisOrder utilities.
115 my @f = split_fields readline $sk;
116 if ($f[1] ne "state") { last LINE; }
117 elsif ($f[2] eq "enable_random") { $st{random} = 1; }
118 elsif ($f[2] eq "disable_random") { $st{random} = 0; }
119 elsif ($f[2] eq "enable_play") { $st{play} = 1; }
120 elsif ($f[2] eq "disable_play") { $st{play} = 0; }
121 elsif ($f[2] eq "resume") { $st{pause} = 0; }
122 elsif ($f[2] eq "pause") { $st{pause} = 1; }
128 my $sk = connect_to_server $C{config};
129 send_command0 $sk, "log";
130 my $st = get_state0 $sk;
135 sub decode_track_name ($\%) {
136 my ($sk, $info) = @_;
137 return unless exists $info->{track};
138 my $track = $info->{track};
139 for my $i ("artist", "album", "title") {
140 my @f = split_fields send_command $sk, "part", $track, "display", "$i";
145 sub format_now_playing (\%) {
147 exists $info->{track} or return "Nothing.";
148 my $r = "$info->{artist}: ‘$info->{title}’";
149 $r .= ", from ‘$info->{album}’" if $info->{album};
150 $r .= "\n(chosen by $info->{submitter})" if exists $info->{submitter};
154 sub get_now_playing ($) {
156 my $r = send_command $sk, "playing";
157 defined $r or return {};
158 my %info = split_fields $r;
159 decode_track_name $sk, %info;
163 sub watch_and_notify0 ($) {
164 my ($now_playing) = @_;
166 my $sk = connect_to_server $C{config}, 1;
167 my $sk_log = connect_to_server $C{config}, 1;
169 send_command0 $sk_log, "log";
170 my $st = get_state0 $sk_log;
171 my $msg = "playing " . ($st->{play} ? "enabled" : "disabled");
172 $msg .= "; random play " . ($st->{random} ? "enabled" : "disabled");
173 $msg .= "; " . ($st->{pause} ? "paused" : "playing");
174 notify "$TITLE state", "Connected: $msg";
175 if ($st->{play} && $now_playing) {
176 my $info = get_now_playing $sk;
177 notify "$TITLE: Now playing", format_now_playing %$info;
180 while (my $line = readline $sk_log) {
181 my @f = split_fields $line;
183 if ($f[1] eq "state") {
185 if ($f[2] eq "disable_random") { $msg = "Random play disabled"; }
186 elsif ($f[2] eq "enable_random") { $msg = "Random play enabled"; }
187 elsif ($f[2] eq "disable_play") { $msg = "Playing disabled"; }
188 elsif ($f[2] eq "enable_play") { $msg = "Playing enabled"; }
189 elsif ($f[2] eq "pause") { $msg = "Paused"; }
190 elsif ($f[2] eq "resume") { $msg = "Playing"; }
191 notify "$TITLE state", $msg if defined $msg;
192 } elsif ($f[1] eq "playing") {
194 $info{track} = $f[2];
195 $info{submitter} = $f[3] if @f > 3;
196 decode_track_name $sk, %info;
197 notify "$TITLE: Now playing", format_now_playing %info;
198 } elsif ($f[1] eq "scratched") {
200 $info{track} = $f[2];
201 decode_track_name $sk, %info;
202 notify "$TITLE: Scratched by $f[3]", format_now_playing %info;
206 notify "$TITLE state", "Lost connection";
212 sub watch_and_notify ($) {
213 my ($now_playing) = @_;
216 claim_lock or exit 1;
219 eval { watch_and_notify0 $now_playing; };
225 ###--------------------------------------------------------------------------
226 ### User-facing operations.
231 sub { run_discard_output "amixer", "sset", $C{mixer}, "5\%+"; };
233 sub { run_discard_output "amixer", "sset", $C{mixer}, "5\%-"; };
235 $OP{"scratch"} = sub {
236 my $sk = connect_to_server $C{config};
237 send_command $sk, "scratch";
241 $OP{"enable/disable"} = sub {
243 my $sk = connect_to_server $C{config};
244 if ($st->{play}) { send_command $sk, "disable"; }
245 else { send_command $sk, "enable"; }
249 $OP{"play/pause"} = sub {
251 my $sk = connect_to_server $C{config};
253 send_command $sk, "enable";
254 if ($st->{pause}) { send_command $sk, "resume"; }
256 if ($st->{pause}) { send_command $sk, "resume"; }
257 else { send_command $sk, "pause"; }
263 if (defined (my $lkpid = locked_by)) {
264 print STDERR "$0: already watched by pid $lkpid\n";
270 $OP{"now-playing"} = sub {
271 my $sk = connect_to_server $C{config};
272 my $info = get_now_playing $sk;
274 print format_now_playing %$info;
278 $OP{"notify-now-playing"} = sub {
279 my $sk = connect_to_server $C{config};
280 my $info = get_now_playing $sk;
282 notify "$TITLE: Now playing", format_now_playing %$info;
283 defined locked_by or watch_and_notify 0;
286 ###--------------------------------------------------------------------------
289 if (@ARGV != 1) { print STDERR "usage: $0 OP\n"; exit 2; }
291 if (!exists $OP{$op}) { print STDERR "$0: unknown op `$op'\n"; exit 2; }
294 ###----- That's all, folks --------------------------------------------------