Term::TtyRec::Plus - read a ttyrec
"Term::TtyRec::Plus" is a module
that lets you read ttyrec files. The related module, Term::TtyRec is
designed more for simple interactions.
"Term::TtyRec::Plus" gives you more
information and, using a callback, lets you munge the data block and
timestamp. It will do all the subtle work of making sure timing is kept
consistent, and of rebuilding each frame header.
use Term::TtyRec::Plus;
# complete (but simple) ttyrec playback script
foreach my $file (@ARGV) {
my $ttyrec = Term::TtyRec::Plus->new(infile => $file, time_threshold => 10);
while (my $frame_ref = $ttyrec->next_frame()) {
select undef, undef, undef, $frame_ref->{diff};
print $frame_ref->{data};
}
}
new()
Creates and returns a new
"Term::TtyRec::Plus" object.
my $ttyrec = Term::TtyRec::Plus->new();
Parameters
Here are the parameters that
"Term::TtyRec::Plus->new()"
recognizes.
- infile
- The input filename. A value of "-",
which is the default, or "undef", means
"STDIN".
- filehandle
- The input filehandle. By default this is
"undef"; if you have already opened the
ttyrec then you can pass its filehandle to the constructor. If both
filehandle and infile are defined, filehandle is used.
- bzip2
- Perform bzip2 decompression. By default this is
"undef", which signals that bzip2
decompression should occur if and only if the filename is available and it
ends in ".bz2". Otherwise, you can force or forbid decompression
by setting bzip2 to a true or false value, respectively. After the call to
new, this field will be set to either 1 if decompression is enabled or 0
if it is not.
- time_threshold
- The maximum difference between two frames, in seconds. If
"undef", which is the default, there is
no enforced maximum. The second most common value would be
10, which some ttyrec utilities (such as
timettyrec) use.
- frame_filter
- A callback, run for each frame before returning the frame to the user of
"Term::TtyRec::Plus". This callback
receives three arguments: the frame text, the timestamp, and the timestamp
of the previous frame. All three arguments are passed as scalar
references. The previous frame's timestamp is
"undef" for the first frame. The return
value is not currently looked at. If you modify the timestamp, the module
will make sure that change is noted and respected in further frame
timestamps. Modifications to the previous frame's timestamp are currently
ignored.
sub halve_frame_time_and_stumblify {
my ($data_ref, $time_ref, $prev_ref) = @_;
$$time_ref = $$prev_ref + ($$time_ref - $$prev_ref) / 2
if defined $$prev_ref;
$$data_ref =~ s/Eidolos/Stumbly/g;
}
State
In addition to passing arguments, you can modify
"Term::TtyRec::Plus"'s initial state, if
you want to. This could be useful if you are chaining multiple ttyrecs
together; you could pass a different initial frame. Support for such
chaining might be added in a future version.
- frame
- The initial frame number. Default 0.
- prev_timestamp
- The previous frame's timestamp. Default
"undef".
- accum_diff
- The accumulated difference of all frames seen so far; see the section on
"diffed_timestamp" in
"next_frame()"'s return value. Default
0.
- relative_time
- The time passed since the first frame. Default
0.
next_frame()
"next_frame()" reads and
processes the next frame in the ttyrec. It accepts no arguments. On EOF, it
will return "undef". On malformed ttyrec
input, it will die. If it cannot reconstruct the header of a frame (which
might happen if the callback sets the timestamp to -1, for example), it will
die. Otherwise, a hash reference is returned with the following fields
set.
- data
- The frame data, filtered through the callback. The original data block is
not made available.
- orig_timestamp
- The frame timestamp, straight out of the file.
- diffed_timestamp
- The frame timestamp, with the accumulated difference of all of the
previous frames applied to it. This is so consistent results are given.
For example, if your callback adds three seconds to frame 5's timestamp,
then frame 6's diffed timestamp will take into account those three
seconds, so frame 6 happens three seconds later as well. So the net effect
is frame 5 is extended by three seconds, and no other frames' relatives
times are affected.
- timestamp
- The diffed timestamp, filtered through the callback.
- prev_timestamp
- The previous frame's timestamp (after diffing and filtering; the originals
are not made available).
- diff
- The difference between the current frame's timestamp and the previous
frame's timestamp. Yes, it is equivalent to
"timestamp - prev_timestamp", but it is
provided for convenience. On the first frame it will be
0 (not
"undef").
- The 12-byte frame header, straight from the file.
- The 12-byte frame header, reconstructed from
"data" and
"timestamp" (so, after filtering,
etc.).
- frame
- The frame number, using 1-based indexing.
- relative_time
- The time between the first frame's timestamp and the current frame's
timestamp.
grep()
Returns the next frame that meets the specified criteria.
"grep()" accepts arguments that are
subroutines, regex, or strings; anything else is a fatal error. If you pass
multiple arguments to "grep()", each one
must be true. The subroutines receive the frame reference that is returned
by "next_frame()". You can modify the
frame, but do so cautiously.
my $next_jump_frame_ref = $t->grep("Where do you want to jump?", sub { $_[0]{data} !~ /Message History/});
rewind()
Rewinds the ttyrec to the first frame and resets state variables
to their initial values. Note that if
"filehandle" is not seekable (such as
STDIN on some systems, or if bzip2 decompression is used),
"rewind()" will die.
infile()
Returns the infile passed to the constructor. If a filehandle was
passed, this will be "undef".
filehandle()
Returns the filehandle passed to the constructor, or if
"infile" was used, a handle to
"infile".
bzip2()
Returns 1 if bzip2 decompression has taken place, 0 if it has
not.
time_threshold()
Returns the time threshold passed to the constructor. By default
it is "undef".
frame_filter()
Returns the frame filter callback passed to the constructor. By
default it is "sub { @_ }".
frame()
Returns the frame number of the most recently returned frame.
prev_timestamp()
Returns the timestamp of the most recently returned frame.
relative_time()
Returns the time so far since the first frame.
accum_diff()
Returns the total time difference between timestamps and filtered
timestamps. "accum_diff" is added to each
frame's timestamp before they are passed to the
"frame_filter" callback.
Shawn M Moore,
"sartak@gmail.com"
- Ttyrecs are frame-based. If you are trying to modify a string that is
broken across multiple frames, it will not work. Say you have a ttyrec
that prints "foo" in frame one and "bar" in frame two,
both with the same timestamp. In a ttyrec player, it might look like these
are one frame (with data "foobar"), but it's not. There is no
easy, complete way to add arbitrary substitutions; you would have to write
(or reuse) a terminal emulator.
- If you modify the data block, weird things could happen. This is
especially true of escape-code-littered ttyrecs (such as those of
NetHack). For best results, pretend the data block is an executable file;
changes are OK as long as you do not change the length of the file. It
really depends on the ttyrec though.
- If you modify the timestamp of a frame so that it is not in sequence with
other frames, the behavior is undefined (it is up to the client program).
"Term::TtyRec::Plus" will not reorder
the frames for you.
- bzip2 support is transparent, mostly. Unfortunately
IO::Uncompress::Bunzip2 is rather slow. I took a lengthy (~4 hours),
bzipped ttyrec and ran a simple script on it, depending on the built-in
bzip2 decompression. This took nearly four minutes. Using bunzip2 then the
same script took about four seconds. So when you can, do explicit bzip2
decompression. Or better yet, help out the guys working on
IO::Uncompress::Bunzip2. :)
Copyright 2006-2009 Shawn M Moore, all rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.