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 )