Term::...::Plus(3pm)

πŸ“¦
version 0.09 Β· 2 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble

✨ NAME ✨

Term::TtyRec::Plus β€” read a ttyrec πŸŽ€

(and yes bestie, the manual page itself is filed under Term::...::Plus(3pm) because man page naming is a whole vibe πŸ’…)


🌸 SYNOPSIS 🌸

okay so Term::TtyRec::Plus is the module that lets you read ttyrec files. Its sibling, Term::TtyRec, is more about simple interactions β€” this one is the upgrade arc πŸ’–. Term::TtyRec::Plus hands you way more info, and with a callback you can munge the data block and the timestamp however you want. It does all the subtle background work of keeping the timing consistent and rebuilding each frame header for you. no cap.

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};
  }
}

πŸ¦‹ CONSTRUCTOR AND STARTUP πŸ¦‹

new()

Creates and returns a brand-new Term::TtyRec::Plus object, like a lil baby ttyrec reader 🍼✨

my $ttyrec = Term::TtyRec::Plus->new();

πŸ’œ Parameters

These are the parameters that Term::TtyRec::Plus->new() recognizes. Periodt.

  • infile β€” the input filename. The value - (which is the default) or undef both mean STDIN. iykyk.
  • filehandle β€” the input filehandle. Default is undef. If you've already opened the ttyrec yourself, you can pass its filehandle right into the constructor. If both filehandle and infile are defined, filehandle wins. πŸ’…
  • bzip2 β€” do the bzip2 decompression thing. Default is undef, which means: decompress if and only if the filename is available and it ends in .bz2. Otherwise you can force it or forbid it by setting bzip2 to a true or false value respectively. After the call to new, this field gets set to 1 if decompression is enabled, or 0 if it isn't. slay, self-documenting.
  • time_threshold β€” the maximum difference between two frames, in seconds. If it's undef (the default) there's 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 the frame gets returned 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 come in as scalar references. The previous frame's timestamp is undef for the first frame. The return value is not currently looked at, okurrr. 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

Besides passing arguments, you can also modify Term::TtyRec::Plus's initial state if you want to. This could be useful if you're chaining multiple ttyrecs together β€” you could pass a different initial frame. Support for such chaining might be added in a future version (manifesting it ✨).

  • 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; peep the section on diffed_timestamp in next_frame()'s return value. Default 0.
  • relative_time β€” the time passed since the first frame. Default 0.

πŸ”₯ METHODS πŸ”₯

next_frame()

next_frame() reads and processes the next frame in the ttyrec. It takes no arguments. On EOF, it returns 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, you get back a hash reference with these fields all filled in:

  • data β€” the frame data, filtered through the callback. The original data block is not made available. sorry bestie.
  • orig_timestamp β€” the frame timestamp, straight out of the file.
  • diffed_timestamp β€” the frame timestamp with the accumulated difference of all the previous frames applied to it. This exists so you get consistent results. For example, if your callback adds three seconds to frame 5's timestamp, then frame 6's diffed timestamp will take those three seconds into account, so frame 6 happens three seconds later as well. The net effect: frame 5 is extended by three seconds, and no other frames' relative times are affected. iconic behaviour, honestly.
  • 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's provided for convenience. On the first frame it will be 0 (not undef). πŸŽ€
  • orig_header β€” the 12-byte frame header, straight from the file.
  • header β€” 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(), every single one of them must be true. The subroutines receive the frame reference that is returned by next_frame(). You can modify the frame β€” but do it cautiously, bestie. ⚠️

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 the state variables to their initial values. Heads up: 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 instead, 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's 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 get passed to the frame_filter callback. πŸ’–


🌈 AUTHOR 🌈

Shawn M Moore, sartak@gmail.com 🫢


πŸ’€ CAVEATS πŸ’€

  • Ttyrecs are frame-based. If you're trying to modify a string that's 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. 😭 that's just how it is.
  • 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's 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. πŸ‘‘ you're on your own for that one.
  • 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.


and that's the whole page, bestie πŸ’œ this one ships as perl v5.32.0, dated 2021-01-07, and it lives in the manual as Term::...::Plus(3pm) β€” go forth and read those ttyrecs, you absolute icon 🌟

πŸ“¦ where this page comes from