App-DrivePlayer
view release on metacpan or search on metacpan
lib/App/DrivePlayer/Player.pm view on Meta::CPAN
}
sub state { $_[0]->_state }
sub current_track { $_[0]->_current_track }
# Called by GUI timer to poll position / detect track end
sub poll {
my ($self) = @_;
return if $self->_state eq 'stop';
return unless $self->_socket;
$self->_drain_events();
my $pos_resp = $self->_send_command_sync(['get_property', 'time-pos']);
my $dur_resp = $self->_send_command_sync(['get_property', 'duration']);
if ($pos_resp && defined $pos_resp->{data} &&
$dur_resp && defined $dur_resp->{data}) {
$self->on_position->($pos_resp->{data}, $dur_resp->{data})
if $self->has_on_position;
}
}
sub quit {
my ($self) = @_;
if ($self->_mpv_pid) {
eval { $self->_send_command(['quit']) };
$self->_close_socket();
# Give mpv a moment to exit cleanly, then force-kill if still running.
my $deadline = time() + 2;
while (time() < $deadline) {
last unless kill(0, $self->_mpv_pid);
sleep 0.1;
}
kill 'KILL', $self->_mpv_pid if kill(0, $self->_mpv_pid);
waitpid($self->_mpv_pid, 0);
$self->_mpv_pid(undef);
} else {
$self->_close_socket();
}
$self->_set_state('stop');
}
# ---- Private helpers ----
sub _bearer_token {
my ($self) = @_;
if (!$self->_token || (time() - $self->_token_time) > $TOKEN_MAX_AGE) {
delete $self->auth->{headers}; # clear cached token to force refresh
my %h = @{ $self->auth->headers() };
$self->_token($h{Authorization});
$self->_token_time(time());
}
return $self->_token;
}
sub _ensure_mpv {
my ($self) = @_;
if ($self->_mpv_pid && kill(0, $self->_mpv_pid)) {
return; # still alive
}
$self->_close_socket();
my (undef, $socket_path) = tempfile("mpv-ipc-XXXXXX", TMPDIR => 1, OPEN => 0);
$self->_socket_path($socket_path);
my $pid = fork() // die "fork failed: $!";
if ($pid == 0) {
exec(
'mpv',
'--idle=yes',
'--no-video',
'--no-terminal',
'--really-quiet',
"--input-ipc-server=$socket_path",
'--gapless-audio=weak',
# Network buffering â reduces stuttering on slow/variable connections
'--cache=yes',
'--demuxer-max-bytes=32MiB', # read-ahead buffer
'--demuxer-max-back-bytes=8MiB',
'--cache-pause=yes', # pause & fill rather than stutter
'--cache-pause-wait=3', # seconds of buffer before unpausing
'--network-timeout=30',
) or die "exec mpv failed: $!";
}
$self->_mpv_pid($pid);
my $deadline = time() + 3;
until (-S $socket_path || time() > $deadline) { sleep 0.1 }
die "mpv IPC socket never appeared" unless -S $socket_path;
$self->_open_socket();
}
sub _open_socket {
my ($self) = @_;
my $sock = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Peer => $self->_socket_path,
) or die "Cannot connect to mpv socket: $!";
$sock->blocking(0);
$self->_socket($sock);
}
sub _close_socket {
my ($self) = @_;
if ($self->_socket) {
eval { $self->_socket->close() };
$self->_socket(undef);
}
if ($self->_socket_path && -e $self->_socket_path) {
unlink $self->_socket_path;
$self->_socket_path(undef);
}
}
sub _send_command {
my ($self, $cmd) = @_;
( run in 1.583 second using v1.01-cache-2.11-cpan-df04353d9ac )