AnyEvent-MPV

 view release on metacpan or  search on metacpan

MPV.pm  view on Meta::CPAN

AnyEvent::MPV - remote control mpv (https://mpv.io)

=head1 SYNOPSIS

   use AnyEvent::MPV;

   my $videofile = "path/to/file.mkv";
   use AnyEvent;
   my $mpv = AnyEvent::MPV->new (trace => 1);
   $mpv->start ("--idle=yes");
   $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
   my $quit = AE::cv;
   $mpv->register_event (end_file => $quit);
   $quit->recv;


=head1 DESCRIPTION

This module allows you to remote control F<mpv> (a video player). It also
is an L<AnyEvent> user, you need to make sure that you use and run a
supported event loop.

MPV.pm  view on Meta::CPAN

   use AnyEvent::MPV;
   
   my $videofile = "./xyzzy.mkv";

   my $mpv = AnyEvent::MPV->new (
      trace => 1,
      args  => ["--pause", "--idle=yes"],
   );

   $mpv->start;
   $mpv->cmd_recv (loadfile => $mpv->escape_binary ($videofile));
   $mpv->cmd ("set", "pause", "no");

   my $timer = AE::timer 2, 0, my $quit = AE::cv;
   $quit->recv;

This specifies extra arguments in the constructor - these arguments are
used every time you C<< ->start >> F<mpv>, while the arguments to C<<
->start >> are only used for this specific clal to0 C<start>. The argument
F<--pause> keeps F<mpv> in pause mode (i.e. it does not play the file
after loading it), and C<--idle=yes> tells F<mpv> to not quit when it does
not have a playlist - as no files are specified on the command line.

To load a file, we then send it a C<loadfile> command, which accepts, as
first argument, the URL or path to a video file. To make sure F<mpv> does
not misinterpret the path as a URL, it was prefixed with F<./> (similarly
to "protecting" paths in perls C<open>).

Since commands send I<to> F<mpv> are send in UTF-8, we need to escape the
filename (which might be in any encoding) using the C<esscape_binary>
method - this is not needed if your filenames are just ascii, or magically
get interpreted correctly, but if you accept arbitrary filenamews (e.g.
from the user), you need to do this.

The C<cmd_recv> method then queues the command, waits for a reply and
returns the reply data (or croaks on error). F<mpv> would, at this point,
load the file and, if everything was successful, show the first frame and
pause. Note that, since F<mpv> is implement rather synchronously itself,
do not expect commands to fail in many circumstances - for example, fit

MPV.pm  view on Meta::CPAN

      $mpv->cmd ("set", "pause", "no");
   });

   $mpv->register_event (end_file => sub {
      my ($mpv, $event, $data) = @_;

      print "end-file<$data->{reason}>\n";
      $quit->send;
   });

   $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));

   $quit->recv;

This example uses a global condvar C<$quit> to wait for the file to finish
playing. Also, most of the logic is now implement in event handlers.

The two events handlers we register are C<start-file>, which is emitted by
F<mpv> once it has loaded a new file, and C<end-file>, which signals the
end of a file (underscores are internally replaced by minus signs, so you
cna speicfy event names with either).

MPV.pm  view on Meta::CPAN

so the movie starts playing. For the C<end-file> event, we tell the main
program to quit by invoking C<$quit>.

This should conclude the basics of operation. There are a few more
examples later in the documentation.

=head2 ENCODING CONVENTIONS

As a rule of thumb, all data you pass to this module to be sent to F<mpv>
is expected to be in unicode. To pass something that isn't, you need to
escape it using C<escape_binary>.

Data received from F<mpv>, however, is I<not> decoded to unicode, as data
returned by F<mpv> is not generally encoded in unicode, and the encoding
is usually unspecified. So if you receive data and expect it to be in
unicode, you need to first decode it from UTF-8, but note that this might
fail. This is not a limitation of this module - F<mpv> simply does not
specify nor guarantee a specific encoding, or any encoding at all, in its
protocol.

=head2 METHODS

MPV.pm  view on Meta::CPAN

sub new {
   my ($class, %kv) = @_;

   bless {
      mpv     => "mpv",
      args    => [],
      %kv,
   }, $class
}

=item $string = $mpv->escape_binary ($string)

This module excects all command data sent to F<mpv> to be in unicode. Some
things are not, such as filenames. To pass binary data such as filenames
through a comamnd, you need to escape it using this method.

The simplest example is a C<loadfile> command:

   $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));

=cut

# can be used to escape filenames
sub escape_binary {
   shift;
   local $_ = shift;
   # we escape every "illegal" octet using U+10e5df HEX. this is later undone in cmd
   s/([\x00-\x1f\x80-\xff])/sprintf "\x{10e5df}%02x", ord $1/ge;
   $_
}

=item $started = $mpv->start (argument...)

Starts F<mpv>, passing the given arguemnts as extra arguments to
F<mpv>. If F<mpv> is already running, it returns false, otherwise it
returns a true value, so you can easily start F<mpv> on demand by calling
C<start> just before using it, and if it is already running, it will not

MPV.pm  view on Meta::CPAN

   my $wbuf;
   my $reqid;

   $self->{_cmd} = sub {
      my $cv = AE::cv;

      $self->{cmdcv}{++$reqid} = $cv;

      my $cmd = $JSON_ENCODER->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 });

      # (un-)apply escape_binary hack
      $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8

      $trace->(">mpv" => $cmd);

      $wbuf .= "$cmd\n";

      my $wcb = sub {
         my $len = syswrite $fh, $wbuf;
         substr $wbuf, 0, $len, "";
         undef $self->{ww} unless length $wbuf;

MPV.pm  view on Meta::CPAN

events. The way this is implemented is to bind a C<client-message> witha
first argument of C<AnyEvent::MPV> and the C<$string> you passed. This
C<$string> is then passed to the C<on_key> handle when the key is
proessed, e.g.:

   my $mpv = AnyEvent::MPV->new (
      on_key => sub {
         my ($mpv, $key) = @_;

         if ($key eq "letmeout") {
            print "user pressed escape\n";
         }
      },
   );

   $mpv_>bind_key (ESC => "letmeout");

You cna find a list of key names L<in the mpv
documentation|https://mpv.io/manual/stable/#key-names>.

The key configuration is lost when F<mpv> is stopped and must be (re-)done

MPV.pm  view on Meta::CPAN

   }

Otherwise, it sets some defaults and loads the file (I forgot what the
C<dummy> argument is for, but I am sure it is needed by some F<mpv>
version):

   $mpv->cmd ("script-message", "osc-visibility", "never", "dummy");
   $mpv->cmd ("set", "vid", "auto");
   $mpv->cmd ("set", "aid", "auto");
   $mpv->cmd ("set", "sid", "no");
   $mpv->cmd ("set", "file-local-options/chapters-file", $mpv->escape_binary ("$mpv_path.chapters"));
   $mpv->cmd ("loadfile", $mpv->escape_binary ($mpv_path));
   $mpv->cmd ("script-message", "osc-visibility", "auto", "dummy");

Handling events makes the main bulk of video playback code. For example,
various ways of ending playback:

      if ($INPUT eq "mpv/quit") { # should not happen, but allows user to kill etc. without consequence
         $status = 1;
         mpv_init; # try reinit
         last;

MPV.pm  view on Meta::CPAN

         $mpv->cmd (set => "bluray-device" => $path);
         $mpv->cmd (loadfile => "bd://");
      } elsif ($moviedir eq "dvd") {
         $mpv->cmd (set => "dvd-device" => $path);
         $mpv->cmd (loadfile => "dvd://");
      }
   } elsif ($type eq "video/iso-bluray") {
      $mpv->cmd (set => "bluray-device" => $path);
      $mpv->cmd (loadfile => "bd://");
   } else {
      $mpv->cmd (loadfile => $mpv->escape_binary ($path));
   }

After this, C<Gtk2::CV> waits for the file to be loaded, video to be
configured, and then queries the video size (to resize its own window)
and video format (to decide whether an audio visualizer is needed for
audio playback). The problematic word here is "wait", as this needs to be
imploemented using callbacks.

This made the code much harder to write, as the whole setup is very
asynchronous (C<Gtk2::CV> talks to the command interface in F<mpv>, which

README  view on Meta::CPAN

NAME
    AnyEvent::MPV - remote control mpv (https://mpv.io)

SYNOPSIS
       use AnyEvent::MPV;

       my $videofile = "path/to/file.mkv";
       use AnyEvent;
       my $mpv = AnyEvent::MPV->new (trace => 1);
       $mpv->start ("--idle=yes");
       $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));
       my $quit = AE::cv;
       $mpv->register_event (end_file => $quit);
       $quit->recv;

DESCRIPTION
    This module allows you to remote control mpv (a video player). It also
    is an AnyEvent user, you need to make sure that you use and run a
    supported event loop.

    There are other modules doing this, and I haven't looked much at them

README  view on Meta::CPAN

       use AnyEvent::MPV;
   
       my $videofile = "./xyzzy.mkv";

       my $mpv = AnyEvent::MPV->new (
          trace => 1,
          args  => ["--pause", "--idle=yes"],
       );

       $mpv->start;
       $mpv->cmd_recv (loadfile => $mpv->escape_binary ($videofile));
       $mpv->cmd ("set", "pause", "no");

       my $timer = AE::timer 2, 0, my $quit = AE::cv;
       $quit->recv;

    This specifies extra arguments in the constructor - these arguments are
    used every time you "->start" mpv, while the arguments to "->start" are
    only used for this specific clal to0 "start". The argument --pause keeps
    mpv in pause mode (i.e. it does not play the file after loading it), and
    "--idle=yes" tells mpv to not quit when it does not have a playlist - as
    no files are specified on the command line.

    To load a file, we then send it a "loadfile" command, which accepts, as
    first argument, the URL or path to a video file. To make sure mpv does
    not misinterpret the path as a URL, it was prefixed with ./ (similarly
    to "protecting" paths in perls "open").

    Since commands send *to* mpv are send in UTF-8, we need to escape the
    filename (which might be in any encoding) using the "esscape_binary"
    method - this is not needed if your filenames are just ascii, or
    magically get interpreted correctly, but if you accept arbitrary
    filenamews (e.g. from the user), you need to do this.

    The "cmd_recv" method then queues the command, waits for a reply and
    returns the reply data (or croaks on error). mpv would, at this point,
    load the file and, if everything was successful, show the first frame
    and pause. Note that, since mpv is implement rather synchronously
    itself, do not expect commands to fail in many circumstances - for

README  view on Meta::CPAN

          $mpv->cmd ("set", "pause", "no");
       });

       $mpv->register_event (end_file => sub {
          my ($mpv, $event, $data) = @_;

          print "end-file<$data->{reason}>\n";
          $quit->send;
       });

       $mpv->cmd (loadfile => $mpv->escape_binary ($videofile));

       $quit->recv;

    This example uses a global condvar $quit to wait for the file to finish
    playing. Also, most of the logic is now implement in event handlers.

    The two events handlers we register are "start-file", which is emitted
    by mpv once it has loaded a new file, and "end-file", which signals the
    end of a file (underscores are internally replaced by minus signs, so
    you cna speicfy event names with either).

README  view on Meta::CPAN

    In the "start-file" event, we again set the "pause" property to "no" so
    the movie starts playing. For the "end-file" event, we tell the main
    program to quit by invoking $quit.

    This should conclude the basics of operation. There are a few more
    examples later in the documentation.

  ENCODING CONVENTIONS
    As a rule of thumb, all data you pass to this module to be sent to mpv
    is expected to be in unicode. To pass something that isn't, you need to
    escape it using "escape_binary".

    Data received from mpv, however, is *not* decoded to unicode, as data
    returned by mpv is not generally encoded in unicode, and the encoding is
    usually unspecified. So if you receive data and expect it to be in
    unicode, you need to first decode it from UTF-8, but note that this
    might fail. This is not a limitation of this module - mpv simply does
    not specify nor guarantee a specific encoding, or any encoding at all,
    in its protocol.

  METHODS

README  view on Meta::CPAN

               sub {
                  warn "$_[0] $_[1]\n";
               }

        on_eof => $coderef->($mpv)
        on_event => $coderef->($mpv, $event, $data)
        on_key => $coderef->($mpv, $string)
            These are invoked by the default method implementation of the
            same name - see below.

    $string = $mpv->escape_binary ($string)
        This module excects all command data sent to mpv to be in unicode.
        Some things are not, such as filenames. To pass binary data such as
        filenames through a comamnd, you need to escape it using this
        method.

        The simplest example is a "loadfile" command:

           $mpv->cmd_recv (loadfile => $mpv->escape_binary ($path));

    $started = $mpv->start (argument...)
        Starts mpv, passing the given arguemnts as extra arguments to mpv.
        If mpv is already running, it returns false, otherwise it returns a
        true value, so you can easily start mpv on demand by calling "start"
        just before using it, and if it is already running, it will not be
        started again.

        The arguments passwd to mpv are a set of hardcoded built-in
        arguments, followed by the arguments specified in the constructor,

README  view on Meta::CPAN

        key events. The way this is implemented is to bind a
        "client-message" witha first argument of "AnyEvent::MPV" and the
        $string you passed. This $string is then passed to the "on_key"
        handle when the key is proessed, e.g.:

           my $mpv = AnyEvent::MPV->new (
              on_key => sub {
                 my ($mpv, $key) = @_;

                 if ($key eq "letmeout") {
                    print "user pressed escape\n";
                 }
              },
           );

           $mpv_>bind_key (ESC => "letmeout");

        You cna find a list of key names in the mpv documentation
        <https://mpv.io/manual/stable/#key-names>.

        The key configuration is lost when mpv is stopped and must be

README  view on Meta::CPAN

       }

    Otherwise, it sets some defaults and loads the file (I forgot what the
    "dummy" argument is for, but I am sure it is needed by some mpv
    version):

       $mpv->cmd ("script-message", "osc-visibility", "never", "dummy");
       $mpv->cmd ("set", "vid", "auto");
       $mpv->cmd ("set", "aid", "auto");
       $mpv->cmd ("set", "sid", "no");
       $mpv->cmd ("set", "file-local-options/chapters-file", $mpv->escape_binary ("$mpv_path.chapters"));
       $mpv->cmd ("loadfile", $mpv->escape_binary ($mpv_path));
       $mpv->cmd ("script-message", "osc-visibility", "auto", "dummy");

    Handling events makes the main bulk of video playback code. For example,
    various ways of ending playback:

          if ($INPUT eq "mpv/quit") { # should not happen, but allows user to kill etc. without consequence
             $status = 1;
             mpv_init; # try reinit
             last;

README  view on Meta::CPAN

             $mpv->cmd (set => "bluray-device" => $path);
             $mpv->cmd (loadfile => "bd://");
          } elsif ($moviedir eq "dvd") {
             $mpv->cmd (set => "dvd-device" => $path);
             $mpv->cmd (loadfile => "dvd://");
          }
       } elsif ($type eq "video/iso-bluray") {
          $mpv->cmd (set => "bluray-device" => $path);
          $mpv->cmd (loadfile => "bd://");
       } else {
          $mpv->cmd (loadfile => $mpv->escape_binary ($path));
       }

    After this, "Gtk2::CV" waits for the file to be loaded, video to be
    configured, and then queries the video size (to resize its own window)
    and video format (to decide whether an audio visualizer is needed for
    audio playback). The problematic word here is "wait", as this needs to
    be imploemented using callbacks.

    This made the code much harder to write, as the whole setup is very
    asynchronous ("Gtk2::CV" talks to the command interface in mpv, which



( run in 0.367 second using v1.01-cache-2.11-cpan-c21f80fb71c )