asciiframe

Je skript, který umožňuje střih asciicastových záznamů pořízených aplikací asciinema, jejich vyčištění od překlepů a vložení dalších událostí.

#!/usr/bin/env bash

Rozcestník

Kód, který zpracovává odchycené čísla chyb

trap '''(
CHYBA=$? ;
case $CHYBA in
 3) echo "Dočasné přerušení skriptu. Pokud se mají zachovat dočasné soubory, použij exit kód 33"
 ;;
 22) echo "ERROR: coreutils - tool for detection absolute path of directory"
 ;;
 25) echo "ERROR: xmlstarlet - tool for work with xml not installed"
 ;;
 29) echo "ERROR: 7z - tool for work with compressed archives is not installed"
 ;;
 33) NORMDIR=yes
 ;;
 37) echo "ERROR: terminator - not installed"
 ;;
 38) echo "ERROR: grep - a basic GNU matching tool not installed"
 ;;
 39) echo "ERROR: sed - GNU stream editor not installed"
 ;;
 40) echo "ERROR: ed - classic UNIX line editor, used for action 'event' not installed"
 ;;
 41) echo "ERROR: jq - flexible command-line JSON processor not installed"
 ;;
esac
) >> /dev/stderr
[ $NORMDIR ] || rm -rf -- "$TEMP_DIR"
exit $CHYBA
''' EXIT

REALPATH=$(command -v realpath)
[ ! ${REALPATH} ] && exit 1
REALPATH="$REALPATH -e "

Aplikace, se kterými pracuje tenhle skript

Aby tenhle skript fungoval jak má, musí být v systému nainstalovány následující aplikace:

ASCIINEMA

I když pro úpravy asciicastu není nezbytně nutná, využívá ji příkaz dump při akci event a také akce play

ASCIINEMA=$(command -v asciinema)

ED

Textový editor ed, se využívá především při akci event

ED=$(command -v ed)
[ ! -x "${ED}" ] && exit 40

GREP

Aplikace grep, se využívá především při akci find

GREP=$(command -v grep)
[ ! -x "${GREP}" ] && exit 38

JQ

Flexibilní nástroj pro práci s formátem JSON v prostředí příkazové řádky (CLI) jq, se využívá ke zpracování hlaviček asciicastových souborů a klipů při akci clip a rovněž při akci view

JQ=$(command -v jq)
[ ! -x "${JQ}" ] && exit 41

SED

Proudový GNU editor sed, je klíčový nástroj se kterým tenhle skript pracuje na všech úrovních

SED=$(command -v sed)
[ ! -x "${SED}" ] && exit 39

MKTEMP

Je nástroj z balíku [gnu.org/software/coreutils coreutils], který se využívá k vytvoření dočasných souborů

MKTEMP=$(command -v mktemp)
[ ! -x "${MKTEMP}" ] && exit 22

${XTERM}

Psedoterminál který se použije při akci play

XTERM=$(command -v xterm)

${TERMINATOR}

Psedoterminál který se použije při akci play

TERMINATOR=$(command -v terminator)

${XDCE4-TERMINAL}

Psedoterminál který se použije při akci play

XFCE4TERM=$(command -v xfce4-terminal)

Přehled proměnných

${APP_NAME} obsahuje aktuální název tohoto skriptu. Pracuje se s ní např. ve funkci help.

APP_NAME="${0##*/}"

${TEMP_DIR}, je dočasný adresář ve kterém se zakládají dočasné, pomocné soubory ${JSONFILE}, ${CLIPFILE}, aj.

TEMP_DIR=$(${MKTEMP} -d --suffix=-asciinema)

${VERSION}, obsahuje číslo verze, které se zobrazuje v logu tohoto skriptu.

VERSION="0.8"

LICENCE

Kontakt:

ales@kapica.cz

Licence:

GNU General Public License (GPL)

${AUTHOR}, je proměnná, která se používá ve funkci logo(), která zobrazuje logo tohoto skriptu, informaci o verzi a copyright.

AUTHOR='(C) 2024 Aleš Kapica - Keny Otter'

Seznam funkcí

Zobrazí se, když se použije parametr -x

logo () {
    (
    echo "                                                                                ";
    echo "    █████  ███████  ██████ ██ ██ ███████ ██████   █████  ███    ███ ███████     ";
    echo "   ██   ██ ██      ██      ██ ██ ██      ██   ██ ██   ██ ████  ████ ██          ";
    echo "   ███████ ███████ ██      ██ ██ █████   ██████  ███████ ██ ████ ██ █████       ";
    echo "   ██   ██      ██ ██      ██ ██ ██      ██   ██ ██   ██ ██  ██  ██ ██          ";
    echo "   ██   ██ ███████  ██████ ██ ██ ██      ██   ██ ██   ██ ██      ██ ███████     ";
    echo "                                                                                ";
    printf '   version: %10s                   %20s      \n'       "${VERSION}" "${AUTHOR}";
    echo "                                                                                ";
    _history
    ) >> /dev/stderr
}

https://discourse.asciinema.org/t/the-plan-for-the-asciinema-cli-3-0/790

Vývoj

S vývojem bylo započato v září 2024, kdy se ukázalo, že stále není k dispozici žádný nástroj, který by umožnil intuitivní střih a úpravu asciicastových záznamů pořízených aplikací asciinema.

Jak jsem zjistil, většinu záznamů, na webu https://asciinema.org, tvoří (až na výjimky) neupravené záznamy. Některé zcela nepochybně vznikly tak, že autor přehrál předem připravený skript. Ale pokud chceme udělat tutoriál ze záznamu, který neměl předem připravený scénář, pořízeného on-the-fly při běžné práci na konzoli, potřebujeme nástroj, který umožňuje nejenom vkládat značky a komentáře, ale také podle potřeby zkracovat či naopak prodlužovat intervalu zobrazení událostí, což vyžaduje přepočítání času zobrazení všech následujících událostí.

Na webu jsem sice našel několik nástrojů pro úpravu asciicastu:

Ovšem ani jeden z nich nesplňoval mé očekávání. Některé jsem ani nezkusil, protože vyžadovaly instalaci dalšího software a já hledal aplikaci, které by stačily jen běžně dostupné utility, jako je sed, grep, atp.

Takže jsem si napsal jednoduchý skript asciiframe_move.sh, který řešil korekci časování událostí (teď akce clip). Pracoval jen se dvěma parametry – číslem řádku události LINE a posunem času SHIFT. Řádky načítal průběžně přes sed a čas zobrazení každé události přepočítával přes bc. Výstup šel na stdout a ten se podle potřeby přesměroval buď do nového souboru, nebo na konec jiného asciicastu.

Při prodloužení intervalu zobrazení, se vypsal celý asciicast, ale při zkrácení jen klip, pro který se použila hlavička výchozího souboru.

Z takto vytvořených klipů bylo možné sestavit nový asciicast – opět se použil skript asciiframe_move.sh, s parametrem SHIFT, kde nastavil čas zobrazení poslední události záznamu, na jehož konec se zapsal výstup skriptu.

Dalších dva skripty, asciiframe_merge.sh a asciiframe_merge.sh, které měly umožnit slučování a dělení událostí již zůstaly nedodělané, protože na základě skriptu asciiframe_move.sh začal vznikat tento skript, který tyhle operace dělá (stejně jako přidávání nových událostí) přes akci event

Počítání času

První velkou změnou, která vedla k výraznému zlepšení výkonu bylo vyřazení aplikace bc, kterou používal výchozí skript i první verze tohoto skriptu k počítání času a její nahrazení funkcemi float_to_string() a float_from_string() které převádí čas.

U asciicastových souborů je totiž čas (v sekundách) zapsán jako číslo typu float, kde je plovoucí čárka nahrazena tečkou. Jenže bash s čísly typu float pracovat neumí. Pracuje pouze s čísly typu integer, bez desetinných míst.

Načtení událostí asciicastu do pole

Další velkou změnou, která přispěla ke zlepšení výkonu tohoto skriptu, bylo načtení událostí do pole ${LINES[@]}.

Původně se události zpracovávaly postupně, ale ukázalo se, že je rychlejší načíst vstupní data rovnou do pole. Nejprve se k tomu používal sed, ale ten začal postupně ve skriptu nahrazovat jq, protože umožňuje rychle a efektivně pracovat s formátem JSON a vyrábět potřebné vstupy.

A protože se ukázalo, že bude nutné od základu přepracovat celý kód tehdy již použitelného a funkčního skriptu, byla tahle verze označena jako 0.1beta

Akce a příkazy

Během vývoje, při implementaci dalších funkcionalit se ukázalo, že je potřeba začít uvažovat jinak, a tak došlo na revizi akcí. Současná logika vychází z logiky asciicastu. Nepracuje se s rámci (frame), jako u videa, ale s událostmi (event). Proto se operace, při nichž se nehýbe s časem událostí následujících řeší příkazy, přes akci event a posuny přes akci clip. Ke zkoumání a prohlížení obsahu asciicastu jsou k dispozici akce find a view, aby nedošlo k neúmyslnému zásahu do záznamu, k jakému by mohlo náhodně dojít při akci #akce_event.

Dokumentace

U verze 0.1 beta již byla syntaxe víceméně ustálena a základní akce které umožňují zpracování asciicastového souboru ustáleny. Zbývalo jen dopsat nápovědy a doplnit dokumentaci

Bohužel průběžné psaní dokumentace je největší brzdou vývoje. Obzvláště zpočátku, protože se kód často mění, vznikají nové funkce a upravují ty stávající, což vyžaduje také změny v dokumentaci.

Tou největší, byla změna formátu dokumentace, která si vynutila kompletní přepsání dokumentace stávající – viz Manuál, ohledně psaní dokumentace, která stále ještě není u konce, protože je spojena s důkladným testováním funkcí.

Vývoj aplikace v datech

Nová funkce splitline() pro dělení zobrazovaného obsahu na jednotlivé znaky (21.10.2024 přejmenovaná na sm()
Nová funkce float_to_string() pro konverzi 'float' řetězce na řetězec 'integer'
Nová funkce float_from_string() pro konverzi 'integer' řetězce na 'float' řetězec (s tečkou)
Nová funkce value() pro rychlé testování parametrů
Pro akci clip (export):
Nová funkce clip_print() pro generování klipu
Pro akci view:
Nová funkce line_m() pro akci
Nová funkce line_o() pro akci view
Nová funkce line_r() pro akci view
Nová funkce lineblock() pro akci view
Přepracovaní funkce value() pro rychlé testování parametrů
Pro akci clip (export):
Nová funkce get_resize()
Nová funkce header_print() pro generování nové hlavičky
Nová akce event, pro práci s událostmi
Změna formátování dokumentace z Markdown syntaxe na syntaxi MediaWiki
Nová funkce include_clip() pro vkládání klipu přes akci clip (import)
Nová funkce get_view() volaná při akci view
Nová funkce findline() volaná při akci find
Nová funkce playcast() volaná při akci play
Nová funkce jqheader() pro načtení hlavičky asciicastu – byla nahrazena 24.10.2024 funkcí header_load()
Nová funkce cast_load() pro načtení událostí
Nová funkce clip_load() pro načtení událostí – viz import asciicastu přes akci clip
Nová funkce print_event() volaná při akci view
Přepracovaná funkce get_view()
Implementace příkazu context
Implementace příkazu split
Přepracovaná funkce jqheader() – byla nahrazena 24.10.2024 funkcí header_load()
Nová funkce print_seconds() volaná při akci event příkazem time
Nová funkce print_time() volaná při akci event příkazem time
Implementace příkazu decolor
Nová funkce print_monochrome()
Přejmenování příkazu 'findtext' na text
Nová funkce events_merge()
Nová funkce get_line()
Rozšíření funkce value() o detekci formátu HH:MM:SS (time)
Přejmenování příkazu poster na frame, který byl 28. 10. 2024 přejmenován na scope
Implementace parametru diff v rámci akce clip
Nová funkce decolor(), pro odbarvení proudu dat pro..
Nový příkazem dump, určený k odbarvení výstupu, produkovaného aplikací asciinema po aplikaci příkazu cat
Nová funkce split_events(), do které byl přesunut výkonný kód příkazu split (akce event)
Opravena funkce get_context(), která používá následující nové, testovací funkce:
Nová funkce line_by_inte() testuje nejde-li řádek mimo záznam
Nová funkce line_by_time() testuje nejde-li čas mimo záznam
Nová funkce line_by_rang() ke zpracování a testování rozsahů, s nimiž budou pracovat příkazy split, merge a které vrací funkce get_scope()
Přejmenování funkce splitline() na sm() a kompletní přepracování
Nová funkce splittext()
Nová funkce do_set()
Nová funkce time_by_line()
Příkazy pro přidání nových událostí byly přesunuty z akce event do nové akce set
Nová funkce do_merge() pro novou akci merge
Přepracován kód pro příkaz delete
Nová funkce float_strip()
Nová funkce float_unify()
Nová funkce set_marker() do které by přesunut kód příkazu set_marker
Dokončen kód příkazu marker u akce set
Implementace příkazu line u akce set
Nová funkce header_header() pro načítání hlavičky asciicastu, která nahradila funkci jqheader()
Přepracovaná funkce header_print()
Je funkční příkaz title akce set
Je funkční příkaz size akce set
Je funkční příkaz duration akce set
Přepracovaná funkce get_string() - pravěpodobně se tím rozbil příkaz text
Příkaz frame byl přesunut mezi příkazy akce #akce_view a následně přejmenován na scope
Oprava funkce delete_line(), která nevypisovala odstraňovaný řádek
Zvýraznění dotazů u příkazů co mění originální ascii.cast: delete, pause, marker, resize, atp.
Rozšíření příkazu time, aby umožňoval nastavení stejného času rozsahu řádků
Oprava příkazu context, aby vracel hodnoty i v případě že mají události shodný čas
Nápověda k akci find a jejím příkazům
Rozšíření funkce do_event() o podporu příkazu decolor
Nová funkce event_decolor()
Nápověda k akci clip a její rozšíření o příkazy clip a play
Rozšíření akce view o příkaz info
Rozšíření funkce clip_print() o podporu rozsahu
Vyřešen import a posun časování přes akci clip
Konverze v1 na v2

Je potřeba...

Zdokumentovat funkci asciiplayer() a také akci play

Byla přepracovaná funkce get_string(), pravděpodobně tím byl rozbit příkaz text, který vyrábí monochromatickou verzi záznamu

Udělat záznam, ve kterém bude aktuální verze skriptu a zároveň demo pro jednotlivé akce Začátek s příklady bude neměnný, tak aby se ten stažený asciicast dal použít rovnou k demonstraci kódu.

Vyhledání události Vložení značky export skriptu z ascii.castu vložení do asciicastu.

Vytvoření klipu se skriptem, který lze naimportovat do jiného asciicastu: 1. export skriptu script.sh do asciicastu script.cast

asciinema rec -c "cat script.sh | grep -v '^# ' | grep -v '^#$'" script.cast

Zjistím, jaký je čas poslední události záznamu script.cast:

$ asciiframe view script.cast -1 1
 ...
102: 101: [0.073059, "o", "... \r\n"]

A přidám za ni značku vymezující konec skriptu:

$ asciiframe set script.cast newline 0.08 AAA-script.sh-AAA

Nyní přidáme značku vymezující začátek skriptu. Není potřeba zjišťovat čas, protože obsahem klipu je pouze skript. Jenom si po vložení značky ověříme, než ji změníme na zobrazitelnou událost, že vložení proběhlo OK

$ asciiframe set script.cast newline 0.000001 VVV-script.sh-VVV
$ asciiframe view script.cast 1 1
 ...
  2:   1: [0.000001, "m", ...
$ asciiframe set script.cast line 2 o
$ asciiframe set script.cast newline 2

asciiframe view
2. vložení značek na začátek a konec
3. konverze značek na typ 'o'
Prohlížení záznamu
Vložení značky
Oprava překlepu
Změna obsahu události
Hromadná změna v obsahu událostí - konverze a optimalizace, aby seděly události v souboru
Export klipu – rozdíl mezi exportem a novou nahrávkou
Import klipu (sloučení přes aplikaci asciinema jdou-li záznamy po sobě.
Odbarvení asciicastu versus odbarvení dump souboru
export skriptu ven z asciicastu

Jak získat asciiframe?

Asciiframe? Asciicast editor from asciicast

Export to asciicast

$ asciinema rec -c "cat asciiframe.sh | grep -v '^# ' | grep -v '^#$'" ascii.cast

Export script from asciicast

$ asciinema cat ascii.cast | tr -d '\r' > asciiframe
$ chmod +x asciiframe
$ ./asciiframe -h

_manual_export_script () {
    echo """
How to find event by string?

    $0 find ascii.cast Text
, can be installed via raw terminal output, if used next filter:

  $ asciinema cat ascii.cast |\\
    sed -n '/^VVV/,/^AAA/p' |\\
    sed '1d;$d' |\\
    tr -d '\r' > asciiframe


How to find event by string?

If line of string is 10, use -10 to delete of it

    $0 event ascii.cast delete -10
    """ >> /dev/stderr
}

Jak vyrobit poster

„Poster” je událost, která překreslí celé terminálové okno svým obsahem, na kterou lze odkázat časem z jejího intervalu zobrazení, než začne obsah okna připisovat následující událost.

Nemusí to být pouze jedna událost. Může to být celá série událostí. Podstatné je, že mají stejný čas, takže se během přehrávání zobrazí všechny najednou. A tohle je postup jak takovou sérii událostí vyrobit.

Možností je několik.

Jednou z nich je export přes akci clip. Nedostane-li tahle akce příkaz clip, exportuje kvůli zachování kontextu také události, které předchází události, kde se začíná něco dít.

Protože mají všechny nastaven čas 0.0, „vysype” je přehrávač všechny najednou, takže bude vypadat výsledek stejně, jako bych přeskočili na ten čas rovnou přes značku (marker).

Pokud odstraníme události, které jsou navíc - ty co už nejsou v okamžiku kdy se souští přehrávání v záběru terminálového okna - získáme klip, jemuž předchází „poster”, Který se bude po naimportování klipu zobrazovat v čase, na který klip vložíme.

Další možnost nabízí příkaz time, který, lze nastavit stejný čas i celému rozsahu řádků. Ideální je, před tento blok událostí přidat také značku - událost typu m (marker), na kterou lze v přehrávači asciicastu rovnou přeskočit.

A   B   C   D   E   F   G

$ asciiframe event ascii.cast time TIME B-F

Po aplikaci tohoto příkazu, by to v rámci časové osy událostí vypadalo - při nastavení času TIME stejného, jaký měla událost na řádku F - asi takto:

A               BCDEF   G

Takto vytvořený blok událostí BCDEF lze spojit do jedné příkazem merge, ale také se dá opět rozdělit, příkazem pause. A to tak, že se těm událostem postupně opět nastaví intervaly zobrazení. A to rozdělením jejich současného intervalu.

$ asciiframe event ascii.cast pause LINE-B 1
AB               CDEF   G

Pokud bych události z řádku B nastavili výše uvedenou hodnotu 1 (maximální využití intervalu), přistrčili bychom ji bezprostředně za událost na řádku A, což ale nechceme. Takže abychom ji vrátili víceméně na původní čas, musíme nastavit jen poměrnou část intervalu

$ asciiframe event ascii.cast pause LINE-B 0.8
A   B             CDEF   G

Poměr postupně snižujeme, protože se celková délka intervalu, tím jak nastavujeme intervaly zobrazení, postupně zkracuje

$ asciiframe event ascii.cast pause LINE-C 0.77
A   B   C           DEF   G

$ asciiframe event ascii.cast pause LINE-D 0.73
A   B   C   D       EF   G

.. až do okamžiku, kdy nastavíme čas události na řádku E tak, aby její interval zobrazení byl stejně dlouhý jako za událostí z řádku D.:

$ asciiframe event ascii.cast pause LINE-E 0.5
A   B   C   D   E   F   G

S nějakou přesností si nemusíme lámat hlavu. Během přehrávání stejně pouhým rozdíl v délce intervalu poznáme, jen pocitově.

_manual_set_poster () {
    echo TODO-manual-set-poster-in-english
    return
}

Manuál, jak psát nápovědy k tomuto skriptu

První řádka nápovědy je prázdná. Po ní následuje obecná ukázka příkazu, a pod ní popis.

Manuál, jak psát dokumentaci k tomuto skriptu

Toto je manuál k tomu, jak se má dokumentovat kód tohoto skriptu. Jakým způsobem se s ním pracuje, je zdokumentováno v rámci funkce help.

Syntaxe a pravidla pro psaní dokumentace

# Prostý komentář se stane součástí dokumentace, pokud je na řádku
# co začíná znakem #, následovaným jednou mezerou.

#Komentář, který bude následovat bezprostředně za tímto znakem,
#bude v dokumentaci interpretován jako součást dokumentovaného kódu
  echo """Stejně jako každý jiný řádek"""
  # Včetně zakomentovaných řádků, kterým předchází mezera či tabulátor
# <!--
# Ale pokud bude zakomentovaný blok, ošetřen jako HTML komentář,
     # bude při zobrazení vygenerovaného HTML skryt, bez ohledu na to
     # jde-li o komentář, nebo
     echo "funkční kód"
# -->

Takovým způsobem jsou ošetřeny například pomocné funkce, které slouží výhradně k rychlé orientaci v kódu.

Úzus

Pro úrovně textových oddílů je následující:

== vymezuje hlavní bloky textu,
=== je úroveň pro dokmentaci funkcí a proměnných,
==== je úrovní pro akce a pododdíly,
===== je úrovní pro vše ostatní

Pro vizuální odlišení odkazů dodržujte pravidlo že:

Odkazy na proměnné, se odlišují strojopisem a obsahují znak $: [[#$VAR_FOO|$VAR_FOO]]

Odkazy na funkce se zobrazují jako běžný text, ale za zdvojenou hranatou závorku se umísťují dvě kulaté závorky: [[#lineblock|lineblock]]()lineblock()

Odkazy na akce se zobrazují kurzívou a obsahují v názvu cíle prefix akce_, viz např. odkaz na akci: ''[[#akce_set|set]]''set

Odkazy na příkazy obsahují v názvu cíle prefix akce, pod kterou patří, např. : [[#set_line|line]]line

Odkazy na volby, přes které se nastavují globální proměnné, se zvýrazňují tučně, např. : '''[[#HELP|-h]]'''-h

A kód, který se má zobrazit v dokumentaci jako ukázkový musí být ošetřen tagy <nowiki> … , až uvnitř párového tagu, <code> … </code>

Dokumentace, se umísťuje vždy před dokumentovaný kód a v jednom zakomentovaném bloku je popis i příklady použití.

Odkazy a kotvy

Většina odkazů rámci této dokumentace je lokálních, takže pokud se odkazuje na funkci help, neodkazuje se na wiki stránku s názvem help, ale na lokální kotvu v rámci téhle vygenerované HTML stránky. Proto je nutné psát za zdvojenou hranatou závorkou [[ znak #, bezprostředně za ním název kotvy a teprve za svislítkem text odkazu.

Tedy ne [[help]] ale [[#help|help]]

Odkazovat lze, tak jako u běžné MediaWiki také na nadpisy, ale je lepší se tomu vyhnout, protože to má svá specifika. Např. to, že pandoc při generování id, ale všechna velká písmena převede na malá.

Mnohem závažnější komplikací je ale to, že se při překladu dokumentace do jiné jazykové verze automaticky generované id změní, proto je lepší psát kotvy rovnou jako html kód:

<span id="kotva">x</span>, kde je místo 'x' text na na který se lze přes 'id' 'kotva' odkazovat.

Maximální počet znaků na řádce

Obsah řádku by neměl přesáhnout 72 znaků. Delší řádky komplikují čtení a prohledávání kódu. A delší text, může bez problému pokračovat na další řádce, protože se při generování html tak jako tak řádky, které jdou za sebou spojí do jednoho odstavce.

V případě funkčního kódu lze rozložit příkazy na více řádků tak, že na konec řádku přidá zpětné lomítko \, viz následující sekvence, kterou se generuje z tohoto skriptu přes aplikaci pandoc statická HTML stránka.

 cat asciiframe.sh | \
     sed 's/^/\ /' | \
    sed 's/\ #$//' | \
   sed 's/\ #\ //' | \
   pandoc -f mediawiki -w html -o asciiframe.html

Výsledkem je statická HTML stránka, u které není možné tak jak je zvykem u MediaWiki využít šablony. proto je nutné při psaní dokumentace dodržet všechna výše uvedená pravidla

help()

Tento skript byl vytvořen pro zpracování asciicastových záznamů uploadovaných na server Thewoodcraft.org.

Ten jede na systému MediaWiki, proto se i pro dokumentaci, která je součástí tohoto kódu používá mediawiki syntaxe.

Tohle je funkce pro nápovědu. A její součástí není jen kód, co se vypisuje na terminál po předání parametru $1, ale i podrobný popis, ke každé akci, včetně ukázkového kódu, ze kterého se generuje stránka s dokumentací.

Funkce samotná, pokud nemá předaný žádný parametr $1 vypisuje globální nápovědu, kde je uveden seznam implementovaných akcí, včetně informace, jak si vypsat nápovědu pro konkrétní akci.

help () {
    case ${1} in
        event)

event

V rámci akce event se spouštějí příkazy, které lze vykonat, aniž by bylo nutné řešit posun časování následujících událostí. Příkazy, které do asciicastu nic nového nepřidávají a řeší jenom události, kterých se příkaz bezprostředně týká:

Událost, ve které je hledaný řetězec zapsaný jako součást textu, lze vyhledat přes akci find, ale zvýraznit ho bude možné až poté se se její obsah příkazem split rozdělí. Nic ale nebrání tomu, obsah původní události, po vložení sekvencí co se postarají o zvýraznění příslušného místa, opět scelit příkazem merge.

Užitečným příkazem akce event je

Časy se kterými skript pracuje jsou v sekundách (a mikrosekundách), což může představovat pro ty, co jsou zvyklí dělit čas na minuty a celé sekundy problém. Hodnota 1, předaná jako parametru totiž není jedna sekunda, ale pořadové číslo události, nebo řádek. Jako čas je akceptováno číslo 1.0, jenže pokud počet sekund překročí hodnotu 60.0, může někomu dělat problém uvědomit si, jak dlouho takový interval trvá. Proto je k dispozici příkaz time, který umožňuje hodnoty času (zaokrouhlené na celé sekundy) oběma směry převádět. Takže hodnota 9999.999999 vrátí 02:46:39, ale obráceně bude výsledek o necelou sekundu kratší 9999.000000

        echo """
    asciiframe [-h] event ascii.cast COMMAND [EVENT [PARAM]]

The 'event' action only affects EVENTS identified by LINE (the event line
in the ascii.cast file) TIME or LINE RANGE. Does not affect the others
events timing. For command help use the '-h' option.

COMMANDS:
   'delete' - remove event
  'decolor' - print asciicast as monochrome asciicast
     'dump' - decolorize RAW output
    'merge' - concate the range or set of events into one event
    'split' - split event into range or set of events
     'time' - conversion string (integer|float) <=> hh:mm:ss and set
              time for event or rang of events.
""" >> /dev/stderr
            ;;
        event_delete)
            echo """
    asciiframe event ascii.cast delete LINE|TIME|RANG [LIMIT]

Command remove from ascii.cast file events. Next example demonstrate how to
remove last event:

  $ asciiframe event ascii.cast delete -0

Nex example demonstrate how to remove scope from line 20 to 22:

  $ asciiframe event ascii.cast delete 20-22

LIMIT is count or interval of time in seconds. Next example demonstrate
how remove scope from last event before time 0:14 (14.0 sec.) to first
event after time 16.0:

  $ asciiframe event ascii.cast delete 0:14 2.0
""" >> /dev/stderr
            ;;
        event_decolor|event_dump)
            echo """
Command decolor merge singlechars events into strings,
remove every sequency what do change font properties and print output
to stdout as the asciicast.

  $ asciiframe event ascii.cast decolor > monochrome.cast

Command dump first apply to the 'ascii.cast' asciinema command 'cat',
and before print output to stdout, apply own function decolor to remove all
sequencies changing the font properties. And send as monochrome output to
stdout, which can be redirected into file

  $ asciiframe event ascii.cast dump > monochrome.raw

File monochrome.raw can be transformed by sed into normal ASCII text – for
example see egg for action split. Next example demonstrate
decolorization raw output from another apps by dump command

  $ asciinema cat color.cast > color.raw
  $ asciiframe event ascii.cast color.raw dump > decolor.raw

File ascii.cast ignore, decolor apply only to output from color.raw file
""" >> /dev/stderr
            ;;
        event_merge) help_line
            echo """Example

    $0 event ascii.cast merge 12.34:5

If line 5 from range, and 12.34 is time from context of this range, be
concated into one event, with time 12.34

WARNING:
    If line of event is a long, may be it the problem. For this use
    next variant:

    $0 event ascii.cast merge 12-15

Concate events from line 12 to 15 into one event and use time of line 12

    $0 event ascii.cast merge 12.34:12-15

Concate events from line 12 to 15 into one event and use time 12.34 for
it, if is from range of concated events

    $0 event ascii.cast merge 12-15:12.34

This variant is for situation when line 12 is from range. Merge do
concation with content lines 14 and 15, but use time 12.34 if is from
range of concated events, but biggest then time of line 12.
""" >> /dev/stderr
            help_line
            ;;
        event_pause)
            echo """Example
""" >> /dev/stderr
            ;;
        event_split) help_line
            echo """Example

    $0 event ascii.cast split 12

Split event from line 12 into range of events with the same time

    $0 event ascii.cast split 12 0.3

Split event from line 12 into set of events, and use interval 0.3 to
set new time

    $0 event ascii.cast split 12-15

Split events from line 12 to 15 into set events and use time from line
12 for every them. After merge of this range it be as 'poster'
""" >> /dev/stderr
            help_line
            ;;
        event_time)

time

~$ asciiframe event ascii.cast time TIME [INTERVAL]

Příkaz time má dvojí funkci. Pokud není předán žádný INTERVAL, převádí čas TIME. Je-li v sekundách, zobrazí ho v „humánním” formátu [HH:]MM:SS, a obráceceně. Víz následující ukázkový příklad:

$ asciiframe event ascii.cast time 85.000000
1:25
$ asciiframe event ascii.cast time 1:25
85.000000

INTERVAL je buď LINE nebo RANG (rozsah řádků) a pokud je uveden, nastaví se TIME - pokud spadá do jejich časového intervalu - všem událostem na těchto řádcích. Události, které se spouští ve stejný čas, jsou tzv. rozsahy.

Ty během nahrávání záznamu nevznikají, protože asciinema zapisuje to co se zobrazí v jeden čas do jedné události. Taková událost je ovšem z hlediska dodatečného střihu dosti nepřehledná, proto tenhle skript umožňuje přes akci split takové události rozdělit a tak se v asciicastu objeví rozsahy událostí, co mají stejný čas. Z hlediska přehrávání se nic nemění, protže se zobrazí najednou, stejně jako kdyby byly součástí jedné události.

Jako jeden rozsah vypisuje při exportu události co předcházejí první události klipu akce clip. Je to proto, aby zůstal zachován kontext klipu, který lze dodatečně odstranit příkazem delete.

Tento příkaz není pod akcí set ptotože pracuje s již existujícími událostmi. A na rozdíl od clip neřeší posun následujících událostí, protože dovoluje nastavit jen čas, který je v rozsahu událostí kterých se ta změna týká. Nelze nastavit čas zobrazení dřívější, ani pozdější, než je čas událostí, které rozsahu předchází a které po něm následují.

Rozsah lze bez problému kdykoliv „rozbít” příkazem pause, který pracuje s časovým INTERVALEM rozsahu.

            echo """
    asciiframe event ascii.cast time TIME [INTERVAL]

Command set same TIME to every event by the INTERVAL scope of events.
INTERVAL is LINE or RANG of lines. If not passed, command convert TIME
from seconds to „human” format, if passed in seconds..

  $ asciiframe event ascii.cast time 85.000000
  1:25

..or to seconds, if passed in „human” format

  $ asciiframe event ascii.cast time 1:25
  85.000000
""" >> /dev/stderr
            ;;
        action) echo """
Action unknown. Try

    $0 -h
    """ >> /dev/stderr && return 1
            ;;
        clip)

clip

$ asciiframe.sh clip ascii.cast PARAMS [> clip.cast]

Při akci clip se realizují operace, při kterých se pracuje s jedním až dvěma asciicastovými soubory. Je-li předán pouze soubor ascii.cast, bude tahle akce na základě PARAMS exportovat bloky událostí do klipu.

$ asciiframe.sh code ascii.cast clip.cast PARAMS

Je-li předán druhý asciicastový soubor, nebo klip clip.cast, záleží co se bude dít na předaných parametrech, Je-li předán příkaz diff, tak se z nich vytáhnou pouze jejich události (bez časování) a to umožní porovnat, do jaké míry se obsahově shodují, bez ohledu na aktuální časy spouštění těchto událostí.

V případě, že se předá jako parametr čas, nebo číslo řádky události v souboru ascii.cast, přistoupí asciiframe k souboru clip.cast jako ke klipu, který začlení na onu pozici do souboru ascii.cast, a odpovídajícím způsobem upraví časování všech událostí. Klipem může být jak nový záznam aplikace asciinema, tak záznam vyexportovaný původně ze stejného souboru přes clip, doplněný o značky a komentáře.

Parametrizovaný export v kombinaci s parametrizovaným importem umožňuje přeskupit události v jiném pořadí – tedy střih asciicastových záznamů.

Přes clip lze také zkracovat, či naopak prodlužovat intervaly zobrazení událostí, což rovněž vyžaduje změny v časování událostí:

$ asciiframe.sh clip ascii.cast -- PARAMS [> clip.cast]

a klipem ().

přepočítává časování všech událostí Umožňuje:

Pro zrychlení, či zpomalení asciicastu (tedy i vyexportovaného klipu), lze využít rovnou aplikaci asciinema, které lze nastavit rychlost záznamu i přehrávání.

$ asciinema rec -c "asciinema play -s 5 -i 2 ascii.cast" zrychleny.cast

Export událostí do klipu

Klipem, může být jiný záznam aplikace asciinema, ale také asciicast nově sestavený, nebo vyexportovaný z libovolného záznamu přes asciiframe.

Export událostí do klipu ovlivňují následující parametry:

$ asciiframe.sh clip ascii.cast [LINE|TIME [INTERVAL]]

První parametr LINE je číslo řádku události v záznamu ascii.cast, která bude první událostí budoucího klipu. Při exportu je vždy lepší použít číslo řádku události než čas, protože lze tímto způsobem rozdělit i série událostí, které mají v záznamu stejný čas zobrazení. Takovou sérii si můžete sami vyrobit viz postup Jak vyrobit poster.

U delších záznamů, nebo záznamů, kde nějaká mikrosekunda nehraje roli, se dá použít i TIME, přibližný čas ve formátu HH:MM:SS, např. 1:25, který se převede na celé sekundy. Ale pokud chcete vyrobit klip, do kterého již nebude nutné zasahovat, je lepší použít čas v sekundách a mikrosekundách, protože čas 1:25 se interně převede na čas 85.000000 a ten nemusí ideálně vyjít.

Teoreticky nezáleží na tom, která událost bude u klipu ta první, ale prakticky je lepší, pokud klip začíná novým řádkem, nebo překreslením terminálové obrazovky. Takové události se dají i v rámci dlouhého záznamu velice rychle přes akci find – nejlépe přes uživatelské jméno, nebo název adresáře zobrazovaný v #promptu, protože to jsou řetězce, co se zobrazují tak rychle, že je #asciinema do záznamu uloží do jedné události. Ale záznam je možné rozdělit i v místě, kde bylo během nahrávání zaznamenáno zalomení řádku (vyhledáte předáním řetězce 'newline'), nebo překreslení terminálového okna (vyhledáte předáním řetězce 'clear')

Druhý parametr INTERVAL určuje jak velký blok událostí se bude exportovat. I ten může být předán jako čas HH:MM:SS, které se převede na sekundy a připočítá k času výchozí události. Stejně, jako když se předá rovnou v sekundách. Výsledkem bude vždy čas, který se použije k vyhledání události, kterou bude klip končit a pracovat se bude s jejím číslem řádku. Ale jak už bylo zmíněno. Problém může nastat u záznamu, který obsahuje série událostí co mají stejný čas, proto je i v tomto případě lepší rovnou uvést číslo řádku události, kterou má klip končit.

Klip vytvoříte tím, že výstup zapíšete do souboru. Viz ukázkový příklad, kdy se ze souboru ascii.cast vyexportují všechny události, od události na řádku 21, co se zobrazí v následujícím časovém intervalu o délce 1:30

$ asciiframe.sh clip ascii.cast 21 1:30 > clip.json

Pokud by nebyl uveden žádný INTERVAL, vyexportovaly by se do klipu clip.json všechny následující události, až do konce souboru. Výchozí čas spuštění první události exportované do klipu začíná vždy na čase 1.0

Import klipu

$ asciiframe.sh clip ascii.cast clip.json [TIME|LINE [PAUSE]]

Na importu klipu clip.json dojde jen pokud bude předán přesný čas v sekundách, nebo číslo řádku události, která se po vložení posune za poslední událost importovaného klipu'. V takovém případě se automaticky nastaví interval zobrazení za poslední událostíklipu'' stejný jako měla posunutá událost.

Čas první události klipu, který je rovněž souborem typu asciicast se nastaví na uvedený čas TIME, nebo čas, který měla původní událost na řádku LINE, a podle toho se přepočítají i časy všech následujících událostí. Tedy i těch, co budou následovat za vloženým klipem.

Za poslední událostí asciicastu (tedy i klipu) ale není nic, z čeho by se dal vypočítat časový interval jejího zobrazení. Proto je možné, přes parametr PAUSE, předat časový interval, kterým lze upravit hodnotu intervalu zobrazení, která by jinak byla stejná jakou měla událost, před kterou byl klip vložen, protože ta se použije, pokud parametr PAUSE chybí. A protože původní interval zobrazení může být i delší než je žádoucí, je možné předáním záporného času ten interval i zkrátit. Ovšem jen pokud bude PAUZE v sekundách!

Rozdíly mezi asciicasty

Při posunech, exportu, a importu dochází vždy na změnu času zobrazení události.

Čas událostí exportovaných do klipu tak už nikdy nebude stejný, jako byl ve výchozím souboru ascii.cast, protože ten se změní ihned poté, co předně ně vložíte nějaký další klip, nebo nebo když zkrátíte, či naopak prodloužíte interval zobrazení některé události, která jim bude předcházet. Proto většinou nejde zjišťovat rozdíly mezi dvěma asciicasty jednoduše tím, že je rovnou předhodíte nějaké aplikaci. To se dá jen dokud nedojde na změnu časování.

Přesto můžete i poté zjistit, ze kterého záznamu klip pochází, pokud použijete asciiframe jako tzv. wrapper a místo TIME, nebo LINE předáte příkaz diff a jméno aplikace, kterou chete ke zobrazení rozdílů použít. Bez toho se použije obyčejný diff.

$ asciiframe.sh clip ascii.cast clip.json diff [meld|...]

Čísla řádků událostí budou stejná, jenom místo hlavičky se zobrazí výchozí hodnoty pro nastavení velikosti terminálu. Pokud bude u obou záznamů stejná bude hned na prvním řádku shoda. a shody u následujících událostí neomylně ukážou, jestli záznamy mají stejný obsah, nebo ne. Viz následující ukázka, u které je vidět, jak vypadá rozdíl událostí, po aplikaci příkazu split

"80", "24"                                                      "80", "24"
"o", "\u001b"                                                 | "o", "\u001b[?2004h
...
"o", "T"                                                      <
"o", "\r\n"                                                     "o", "\r\n"
"o", "\u001b[?2004l\r\u001b[?2004h> "                         | "o", "\u001b[?2004l\r\u001b[?2004h> Tohle je text komentáře k
"o", "T"                                                      <
"o", "o"                                                      <
"o", "h"                                                      <
"o", "l"                                                      <
"o", "e"                                                      <
"o", " "                                                      <
"o", "j"                                                      <
"o", "e"                                                      <
"o", " "                                                      <
...

Jak upravit časování událostí

Akce clip umožňuje zkrátit, nebo naopak prodloužit interval zobrazení. Každá událost má svůj INTERVAL – čas, který uplyne mezi zobrazením události předchozí, jejím zobrazením a zobrazením události následující.

Její vlastní zobrazení dělí tenhle INTERVAL na dvě části:

Čas, který uplyne než dojde na její zobrazení – to je interval zobrazení události předchozí.
A interval zobrazení, než bude zobrazena událost následující, která může klidně její zobrazený obsah speciální sekvencí příkazů pro terminál překreslit.

Jaké má tyhle intervaly událost si můžeme zobrazit příkazem context, při akci view. A nebuďte zaskočeni, pokud se vrátí samé nuly:

$ asciiframe view ascii.cast context 10
0.000000 (0.000000 - 0.000000)

Událost, která má nulový kontext je totiž součástí série událostí, co mají stejný čas zobrazení, což se ukáže, když při akci view, použijete krátký interval:

$ asciiframe view comment_00.cast 10 0.1
   10:   9: [0.051663, "o", "\u001b"]
   ...
   61:  60: [0.051663, "o", " "]
$ asciiframe view ascii.cast context 61
2.190601 (0.000000 - 2.190601)

Z kontextu poslední události oné série na 61 řádce se dozvíte, že na zobrazení další události, dojde zhruba za 2 sekundy.

Použijete-li záporný interval, dozvíte se, kterou událostí ona série začíná. Ta je, jak je vidět na níže uvedeném ukázkovém výpisu na řádku 2. Což znamená, že se celá série těchto událostí zobrazí ihned po spuštění přehrávání záznamu.

$ asciiframe view comment_00.cast 10 -0.1
    2:   1: [0.051663, "o", "\u001b"]
   ...
   10:   9: [0.051663, "o", "\u001b"]

Pokud bychom chtěli, aby se tento poster zobrazoval déle, můžeme následující události velice jednoduše posunout následujícím příkazem o dalších 5 sekund:

$ asciiframe.sh clip ascii.cast -- 62 5.0

Syntaxe je stejná jako při importu klipu. Rozdíl je pouze v tom, že se místo souboru s klipem předají jen dvě pomlčky.

Při uvedení záporné hodnoty intervalu, se čas zobrazení naopak zkrátí, což se může hodit obzvláště poté, co příkazem decolor vyrobíte odbarvenou, monochromatickou verzi záznamu, která bude mít všechny jednoznakové události sloučené. U takového záznamu je zbytečné, mít dlouhé čekací intervaly, protože se dá přehrávat po jednotlivých událostech.

            echo """
      asciiframe clip ascii.cast -- TIME|LINE [PAUSE]

If not clip file, do only shift events timing in ascii.cast
If get clip.cast file, do import events into ascii.cast to
TIME or before event from LINE. PAUSE used after last imported event.

      asciiframe clip ascii.cast clip.cast TIME|LINE [PAUSE]

If get LINE, TIME or RANG, do export events from ascii.cast
If TIME or LINE event, print all from this events to end of file. If get
RANG, print events only by select.

      asciiframe clip ascii.cast INTERVAL [COMMAND]

COMMANDS:
    clip - By default print to output all events, only set time events
        before selected TIME or LINE time 0.0. Command 'clip' off print
        this events
    play - Asciinema by default don't play record by time select. This
        command is workaround for it
    """ >> /dev/stderr
            ;;
        clip_diff) echo """
How to show differences between ascii.cast and clip.cast

    $0 clip ascii.cast clip.cast diff

or show by another diff app (meld for example)

    $0 clip ascii.cast clip.cast diff meld
"""
            ;;
        clip_float) echo """
Akce 'clip' pracuje pouze s čísly typu float
Chceme-li tedy zkrátit, nebo prodloužit interval zobrazení události o
níž víme že je na řádku č. 56, musíme nejprve zjistit, její čas
    ./asciiframe.sh clip ./prelozit1.json -- 0.3
"""
            ;;
        clip_import)
            echo  """Import klipu

Při importu se u všech událostí co následují za časem na který se vloží
obsah klipu posune časování. Jako  lze použít libovolný záznam
typu asciicast, nebo jeho kus vyexportovaný jako klip.

    $0 clip   POSITION [SHIFT]

POSITION je čas, kladné číslo s tečkou, typu float, na který se má
    klip vložit. Časování následujících událostí se posune podle jeho
    délky trvání.

SHIFT je kladné, ale může být i záporné číslo typu float, které udává
    o kolik se má posunout čas za poslední událostí klipu.
    Je-li záporné, záleží na velikosti prodlevy za poslední zobrazenou
    událostí klipu.
    Při vkládání klipu je doporučeno používat raději kladný SHIT a délku
    zobrazení poslední události následně upravit posunem časování, než
    aplikovat rovnou záporný SHIFT
""" && help clip_move
            ;;
        clip_move) echo  """
    asciiframe clip ascii.cast -- EVENT SHIFT

The 'clip' action without the clip file, only with two chars '--', apply
SHIFT to all events since the EVENT identified by LINE (the event line
in the ascii.cast file) or TIME and thus changes their timing.

All parameters are mandatory. Application changing the duration and set
new timestamp. It's reason why writes the output to stdout and not into
the ascii.cast file!

Negative SHIFT do shortening the waiting interval for the EVENT. And
positive value extend display time after is showed, so the wiewer has
enough time to read.
""" >> /dev/stderr
            ;;
        find)

find

Akce zavolá funkci findline(), předá jí parametry a ta se pokusí pomocí nástroje grep v asciicastu podle nich něco najít. Jenže záznam aplikace asciinema není obyčejný textový soubor. Jsou to sekvence znaků zachycené v čase, tak jak se vypisovaly na terminál, takže mohou vypadat různě. Problém je např. s vyhledáním příkazu, který se psal v okně terminálu přes klávesnici. Přes find se ho nepodaří najít, protože je každé písmeno zaznamenáno jako samostatná událost. Ale existuje způsob, jak to vyřešit – dekomponovat záznam přes split a sloučit textové řetězce přesmerge. Časování událostí se tím nezmění, takže se dá výsledný asciicast (u kterého zmizí efekt přímého psaní na příkazové řádce terminálu) využít k tomu, abychom zjistili, jaký čas nastavit pro vložení značky do výchozího záznamu.

$ asciiframe.sh find ascii.cast STRING|KEY [PARAM]

Pokud se najde STRING, vypíše se událost příslušného řádku stejně jako při akci view a nalezený řetězec bude od odstatního textu barevně odlišen (červeně).

Pro vyhledání řetězců, co obsahují řídící znaky terminálu, nebo mezery je potřeba využít zápisu přes hexa sekvence. Takže místo 'The Book' se použije..

$ asciiframe.sh find ascii.cast 'The\x20Book'

Zástupná klíčová slova

Podobným způsobem se dají vyhledat i speciální sekvence znaků, jimiž se řídí terminál, ale pro zjednodušení se využívají zástupná klíčová slova, která nabízí funkce get_string():

Sekvence pro práci s kurzorem se zaznamenávají během událostí, kdy kurzor (místo kde se vpisuje text) mění v rámci terminálového okna svou pozici.

A toto jsou reálné příklady jejich využití.

Pokud chcete vyhledat veškeré události, při kterých terminál dostal příkaz, aby pokračoval ve výpisu na dalším řádku, můžete použít

$ asciiframe.sh find ascii.cast breakline

Tahle událost však obvykle nebývá osamocená. Pokud jí předcházel stisk klávesy Enter, objevila se v záznamu události také speciální sekvence, kterou lze najít přes zástupné klíčové slovo enter. Události, které ji obsahují, jsou ideálním bodem, od kterého lze následující události exportovat do samostatného klipu, nebo před který lze vložit příkazem marker bod, na který se dá – pokud to podporuje přehrávač asciicastu – během přehrávání přeskočit.

            echo """
    asciiframe find ascii.cast STRING

If found STRING in the asciicast record, colorize it and show event with
a number line and order similary as 'view' action. To search combination
strings with spaces, replace space by string '\x20'

    asciiframe find KEY [PARAM]

PARAM is combination of numbers and semicolons. Unknown value can be
replaced by a dot. Option '-h' show help for KEY with example of using.

KEYS:
       backchar         [cursor|H]            cursor-show
       [backline|K]     [cursor-up|A]         cursor-save
       blink            [cursor-down|B]       cursor-restore
       breakline        [cursor-fore|C]       enter
       [clear|J]        [cursor-back|D]       newline
       color            cursor-hide           return
""" >> /dev/stderr
            ;;

backchar

Vkládá sekvenci \b, která posouvá aktuální pozici kurzoru o jeden znak nazpět.

Při vyhledání sekvence 'backchar' můžeme narazit na událost, při níž se kurzor posunul o několik těchto sekvencí najednou - je to neklamná známka toho, že byla stisknuta klávesa 'Home'­

Typicky se objeví taková událost při úpravě příkazu vytáhnutého z historie, nebo při nakopírování obsahu schránky, protože se změní zvýraznění nakopírovaného textu. Viz příklad.

$ asciiframe.sh find ascii.cast backchar
 7:[0.437473, "o", "\u0007\b\b\b\b\b\b\b\b\bcd pokus/"]
$ asciiframe.sh view ascii.cast 7 -1
 6:   5: [0.356846, "o", "\u001b[7mcd pokus/\u001b[27m"]
 7:   6: [0.437473, "o", "\u0007\b\b\b\b\b\b\b\b\bcd pokus/"]

Z vypsaného kontextu je zřejmé, že text nezměnil, jenom zmizely sekvence barev:

 6:   5: [0.356846, "o", "\u001b[7mcd pokus/\u001b[27m"]

Pokud tedy změníme text události z řádku 6 následujícím způsobem, můžeme událost z řádku 7 s klidnou duší odstranit.

Nejprve změníme typ události z řádku 7 na m (marker)

$ asciiframe.sh set ascii.cast line 6 m
 ...
$ asciiframe view prelozit1.json m
 6:   1: [0.356846, "m", "\u001b[7mcd trans/\u001b[27m"]
 ...
$ asciiframe set prelozit1.json marker 1 'cd trans/'
 ...
$ asciiframe view prelozit1.json 6 1
 6:   1: [0.356846, "m", "cd trans/"]
 7:   5: [0.437473, "o", "\u0007\b\b\b\b\b\b\b\b\bcd trans/"]
$ asciiframe.sh set ascii.cast line 6 o
$ asciiframe view prelozit1.json 6 1
 6:   1: [0.356846, "o", "cd trans/"]
 7:   5: [0.437473, "o", "\u0007\b\b\b\b\b\b\b\b\bcd trans/"]
$ asciiframe.sh event ascii.cast delete 7

Pokud ale chceme, aby v tutoriálu figuroval jiný adresář než ~/pokus, záleží kolik bude mít znaků a podle toho je potřeba upravit obě události. Tak aby počet „umáznutí” odpovídal počtem znaků délce nového řetězce.

        find_backchar)
            echo """
backchar
""" >> /dev/stderr
            ;;

K - backline

Vkládá sekvenci, která smaže obsah řádku, od místa kde se aktuálně nalézá kurzor, aniž by se při tom změnila pozice kurzoru

Není-li žádný parametr, je to stejné jako by byla parametrem 0 - umaže se obsah řádku od aktuální pozice kurzoru, směrem ke konci řádku. Pokud tedy přidáte přes tuhle událost událost, kde bude sekvence \b (#set_backchar), která posune kurzor směrem zpět ku začátku řádky, bude výsledkem vymazání části obsahu terminálové řádky, zobrazené v průběhu předchozích událostí.

Parametrem 1 se mění směr, takže se vymaže naopak obsah před kurzorem a pokud je předána hodnota 2, vymaže se řádek celý a následující události ze budou zobrazovat od aktuální pozice kurzoru.

Pomocí zástupných slov 'backchar' a 'backline' lze vyhledat události, při kterých byla použita klávesa Backspace. Obvykle se jedná o překlepy, které mohou působit rušivě, pokud chceme záznam využít jako tutoriál. Použijeme-li tedy

$ asciiframe.sh find ascii.cast backline

nebo též

$ asciiframe.sh find ascii.cast K

A vrátí se nám v záznamu něco podobného..

24:[13.094431, "o", "\b\u001b[K"]
25:[13.275424, "o", "\b\u001b[K"]
26:[13.637253, "o", "\b\u001b[K"]

..půjde s velkou pravděpodobností o překlep, který můžeme ze záznamu odstranit. Protože každá sekvence '\b' představuje jeden umáznutý znak z řetězce zobrazeného předchozí událostí (která nemusí být jen jedna). Sekvence jakou najde backline totiž vzniká obvykle při psaní, kdy poté, co zjistíme svůj omyl, umázneme text a napíšeme nový. Každé stisknutí klávesy se uloží samostatná událost. Takže pokud odstraníme řádky 24–26 i řádky 21–23, odstraníme překlep ze záznamu.

Ale než to uděláme, pro jistotu si zobrazíme události těchto řádků přes view:

$ asciiframe.sh view ascii.cast 21 26
21:[8.664646, "o", "1"]
22:[9.736504, "o", "6"]
23:[10.604407, "o", "9"]
24:[11.128128, "o", "."]
25:[13.094431, "o", "\b\u001b[K"]
26:[13.275424, "o", "\b\u001b[K"]
27:[13.637253, "o", "\b\u001b[K"]

A protože je tomu vskutku tak, Odstraníme tyhle události příkazem

$ asciiframe.sh event ascii.cast delete 21 26

        find_K|\
        find_backline)
            echo """
    asciiframe find ascii.cast K [DIGIT]

Search sequences EL (Erase in Line), which erases part of the line. If
DIGIT is 0 or missing, clear line from cursor to end of line. If
DIGIT is 1, clear line from cursor position to beginning of the line.
DIGIT is 2 clear entire line. Cursor position does not change.

Example of search [K

  $ asciiframe find ascii.cast K

This sequence commonly exists with a combination with a new line,
backchar or return sequence, can be used next string for search, whereis
'n' (newline) may be replaced by 'b' (backchar) or 'r' (return):

  $ asciiframe find ascii.cast '\\n\\u001b[K'
""" >> /dev/stderr
            ;;

J - clear

        find_J|\
        find_clear)
            echo '''
    asciiframe find ascii.cast J [DIGIT]

Search sequences ED (Erase in Display), which clears part of screen. If
DIGIT is 0 or missing, clear screen from cursor to end of screeen. If
DIGIT is 1, clear screen from cursor position to beginning of the screen.
DIGIT is 2 clear entire screen, and move cursor to upper left corner. If
DIGIT is 3 clear entire screen and empty the scrollback buffer.

Example of search [2J

    asciiframe find ascii.cast J 2
''' >> /dev/stderr
            ;;
        find_blink)
            echo '''
 blink - not alternative to color
''' >> /dev/stderr
            ;;
        find_colon) help_line
            echo """
Parameter keys of find not accept colon (:), only semicolon (;)
""" >> /dev/stderr
        ;;

color

        find_color)
            echo """
    asciiframe find ascii.cast color [STRING]

Search sequences for change font properties. If not STRING, show events
where string [m return font properties to defaults. Its same
effect as sequence [0m, which can be founded by

    $ asciiframe find ascii.cast color 0

 1 - set bold text
 3 - set underline text
 4 - set cursive text

More complexity strings can be founded as combination DIGITS & semicolon

    $ asciiframe find ascii.cast color '..;.'

Found strings as [31;1m, [32;3m & etc., because dot substitute unknown
DIGIT. Color can be set by RGB over sequence 38;2;RED;GREEN;BLUE where
RED, GREEN and BLUE values specify foreground color.
""" >> /dev/stderr
        ;;

Cursor

        find_A|\
        find_B|\
        find_C|\
        find_D|\
        find_H|\
        find_cursor|\
        find_cursor-fore|\
        find_cursor-back|\
        find_cursor-up|\
        find_cursor-down)
            echo '''
Terminal sequence move the cursor in screen area by PARAM and prepare
situation to be rewrite this part of terminal window by new content

Command A, or cursor-up N rows
Command B, or cursor-down N rows
Command C, or cursor-fore N columns
Command D, or cursor-back N columns

Command H, or cursor accept one or two values: LINES COLUMNS. If actually
size of terminal, is 80x24, find next command sequence set cursor to left
bottom corner: [80;1H, but for search events where the cursor
changed position is better use dots:

    $ asciiframe find ascii.cast cursor '..;.'
''' >> /dev/stderr
        ;;

cursor-show & cursor-hide

        find_cursor-show|\
        find_cursor-hide)
            echo '''
    $0 find ascii.cast cursor-show 1

5 flash on/of
25 VT220
1002
1006
1004 enable/disable reporting status
1049 enable/disable screen buffer
2004 turn on/off paste mode
    $0 find ascii.cast cursor-show 2...
''' >> /dev/stderr
        ;;

Co je asciicast?

Asciinema zaznamenává všechny události, co se dějí v terminálovém okně do souboru, zvaného asciicast, kde má každá událost samostatný řádek ve formátu JSON.

U starší verze 1 je to jinak. U něj je každá událost součástí pole 'stdout'. Proto doporučujeme pro záznamy používat příponu .cast. Starší verze záznamů používaly příponu .json

Obě verze formátu mají na prvním řádku souboru hlavičku. Takto vypadá záhlaví verze 2:

{
 "verze": 2,
 "šířka": 80,
 "výška": 24,
 "časové razítko": 1725352572,
 "env": {
   "SHELL": "/bin/bash",
   "TERM": "xterm-256color"
  }
}

Atributy nezbytné
version - Verze asciicastu je důležitá pro přehrávač. CLI přehrávač podporuje pouze formát v1 a v2. Pro novější verzi formátu se v3 nepoužívá - atributy, s nimiž pracuje javascriptový přehrávač asciinema během přehrávání ignoruje. Ale prostřednictvím asciiframe s nimi můžeme pracovat.
width & height - nastavují výchzí velikost terminálového okna.

Novější verze formátu podporuje události typu resize která umožňuje velikost okna za běhu změnit a marker, což jsou značky na které lze rovnou během přehrávání přeskočit.

Atributy volitelné
timestamp - Obsahuje časové razítko okamžiku, kdy byl záznam pořízen
duration - Celkový čas záznamu (float) obvykle chybí, ale může být doplněn.
idle_time_limit - Čas prodlevy mezi položkami. Zadává se při spouštění záznamu. Podle toho se pak zaznamenávají události.
commad - Asciinema pracuje s těmito příkazy:
rec
play
cat
upload
auth
title - Pojmenování záznamu. Zadává se obvykle při začátku nahrávání, ale je možné ho pojmenovat dodatečně, přes akci 'title'. U klipů se nastavuje title automaticky, podle položky MARKER, nebo výchozího času při akci 'move' Při akci 'clip', se naopak použije tenhle parametr pro vytvoření ITEM položky typu MARKER
theme - Dovoluje změnit výchozí nastavení barev terminálu.

        file)
            echo """
    Description of asciinema format

In asciinema format v2 is every terminal event recorder in the asciicast
file a independent row as JSON formated. Older format version 1, is
different - every event is part of array 'stdout'.

For v2 record is .cast suffix recommend. Early versions of
records v1 commonly .json use.

First line in both versions is for HEADER. Its example header of v2:

"""'''
    {
        "version": 2,
        "width": 80,
        "height": 24,
        "timestamp": 1725352572,
        "env": {
            "SHELL": "/bin/bash",
            "TERM": "xterm-256color"
        }
    }
'''"""

NOTE: Old format version 1 don't use 'timestamp' - time of record.

Attributes required:

version - Verze asciicastu je důležitá pro přehrávač. CLI přehrávač
        podporuje pouze formát v1 a v2. Formát v3 se kterým pracuje
        javascriptový přehrávač ignoruje. Ovšem ignoruje také položky
        ITEM jiného typu, takže s nimi můžeme v rámci v2 formátu pracovat.

width & height - Určují výchozí velikost terminálu. Verze v3
                 který se má použít. Tyhle hodnoty se dají za běhu změnit
                 pomocí rámce 'r', který obsahuje např. '60x30'

Atributy volitelné:

timestamp - Obsahuje časové razítko okamžiku, kdy byl záznam pořízen

duration - Celkový čas záznamu (float) obvykle chybí, ale může být doplněn.

idle_time_limit - Čas prodlevy mezi položkami. Zadává se při spouštění záznamu.
        Podle toho se pak zaznamenávají události.

commad -
        rec
        play
        cat
        upload
        auth

title - Pojmenování záznamu. Zadává se obvykle při začátku nahrávání, ale
        je možné ho pojmenovat dodatečně, přes akci 'title'. U klipů se
        nastavuje title automaticky, podle položky MARKER, nebo výchozího
        času při akci 'move'
        Při akci 'clip', se naopak použije tenhle parametr pro vytvoření
        ITEM položky typu MARKER

theme - Dovoluje změnit výchozí nastavení barev terminálu."""'''

        "theme": {
            "fg": "#d0d0d0",
            "bg": "#212121",
            "palette": "#151515:#ac4142:#7e8e50:..."
        }
'''"""
        fg - výchozí barva písma
        bg - výchozí barva pozadí
        pallete - seznam hexakódů (#rrggbb) 8 nebo 16 barev, oddělených
            dvojtečkou

env - nastavuje shell a jeho barevné  schéma

Události ITEMS

* Při záznamu se ukládá pouze výstup terminálu (out), jako FRAME 'o'
* Dodatečně lze záznam rozdělit pomocí události MARKER 'm' tak, aby bylo
    možné v javascriptovém přehrávači přeskakovat na konkrétní časy
* Kromě toho se může vyskytnout také událost typu 'r', kdy dojde ke změně
    výchozích rozměrů okna terminálu. Ovšem při přehrávání záznamu na
    konzoli se to ignoruje.
""" >> /dev/stderr
            ;;

merge

TODO

        event_merge)
            echo """
Při akci merge musí být předány minimálně dva argumenty, Identifikátor
zobrazované události (čas, nebo číslo řádku) a čas, nebo počet rámců,
kterým se vymezí rozsah zobrazovaných zpráv, jaký se má sloučit.

    $0 merge  <ORDER|TIME> <INTERVAL|COUNT>
""" >> /dev/stderr
            ;;

split

U záznamů, které obsahují rozsáhlé výpisy a hodně barev je výhodnější rozdělit události jenom tam, kde chceme udělat střih, nebo něco zvýraznit. Optimalizace celého záznamu přes akce split a ''merge totiž může být velmi zdlouhavá, a výsledný soubor velmi velký, protože se pro každý zobrazovaný znak generuje samostatná událost.

Následující příklad, kde je tučným písmem zvýrazněno použití příkazu split demonstruje jak to funguje:

$ asciiframe view ascii.cast 2
  2:   1: [0.051663, "o", "\u001b[?2004h\u001b]0;user@stroj: 
       |       \u0007user@stroj:~$ "]
$ asciiframe event ascii.cast split 2
$ asciiframe view ascii.cast context 2
0.051663 (0.051663 - 0.000000)
$ asciiframe view ascii.cast 10 0.1
    2:   1: [0.051663, "o", "\u001b[?2004h"]
    3:   2: [0.051663, "o", "\u001b]0;"]
    4:   3: [0.051663, "o", "u"]
    5:   4: [0.051663, "o", "s"]
   ...
   32:  31: [0.051663, "o", " "]
$ asciiframe view ascii.cast frame
0.051663:2-32
   ...

Na řádku 2 má každý asciicast první událost, která začíná sekvencemi \u001b[?2004h (ukaž kurzor) a \u001b]0; (vyresetuj původní obsah terminálu) po které se začíná psát do terminálu.

Při záznamu asciinema ukládá dění na terminálu do tzv. událostí, realizovaných v určitý konkrétní čas.

V případě tzv. zobrazitelných událostí, se obsah události v uvedený čas vypíše na standardní terminálový výstup. Může to být jeden znak, ale i celá sekvence znaků a pomocí akce split, lze tuhle událost rozdělit na sérii dílčích akcí, což ve výsledku vyvolá dojem, jako by nám onu sekvenci někdo právě psal.

Takovým způsobem lze rozdělovat pouze zobrazitelné události. Ten se liší tím, že aplikuje funkci text jen na omezený rozsah řádků a výsledek zapisuje do originálního souboru. Více viz dokumentace příkazu split.

        event_split)
        echo """Nápověda pro split
    $0 clip  START [INTERVAL]

START - identifikuje zobrazitelnou událost, která se má rozdělit na dílčí
    události. Událost lze identifikovat časem, ve kterém dojde na její
    zobrazení, nebo je v intervalu, který jí předchází.
    Nebo číslem řádky na které se nachází.

INTERVAL - je kladné či záporné číslo typu float. V případě, že jde o číslo
    kladné, použije se jako výchozí interval zobrazení.
    Bude-li číslo záporné, vyjde se z délky stávajícího intervalu zobrazení
    tedy času který uplyne, než dojde na zobrazení následující události,
    od kterého se předaný čas odečte.
    To znamená, že interval zobrazení mezi znaky bude takový, aby zůstal
    zachován čas zobrazení předaný coby záporný interval.
""" >> /dev/stderr
            ;;
        merge)
            echo """
TODO help merge
""" >> /dev/stderr
            ;;
        play)

play

Spustí přehrávání záznamu v novém terminálovém okně, jehož velikost přizpůsobí velikosti terminálu uvedené v asciicastu

Současný kód preferuje xfce4-terminal, protože umožňuje měnit za běhu velikost fontu, což umožňuje přehrávání záznamů ze strojů s velkým rozlišením displeje

            echo  """
Open new terminal window, sized by size of the ascii.cast and start play

    $0 play ascii.cast
""" >> /dev/stderr
            ;;
        'set')

set

Pokud nahráváte své asciicasty jen proto, abyste si mohli dodatečně přehrát, co se dělo, nepotřebujete do nich zasahovat, ale pokud chcete aby si ho přehrál někdo jiný, v přehrávači který podporuje přeskoky na události typu MARKER, určitě využijete příkaz marker, Přes který lze na konkrétní čas vložit do záznamu událost, na kterou lze během přehrávání asciicastu přeskočit.

To, zda nějaké události tohoto typu v záznamu existují, zjistíte přes akci view, když se jí předá parametr m, ale změnit zobrazovaný text, můžete jedině tak, že přidáte příkazem marker na stejný čas značku s novým obsahem. Interně se pak zavolá příkaz delete, který patří mezi příkazy akce event.

Akce set umožňuje vkládat do záznamu i sekvence s příkazy pro terminál, které umožní například nějakým způsobem zvýraznit určitý řetězec.Např. příkazem color lze přidat před zobrazovanou událost, kterou se zapne barevné (či jiné) a příkazem asciicastu do tutoriálu, se mohou hodit i další příkazy:

        echo '''
    asciiframe [-h] set ascii.cast COMMAND [TIME|ORDER] [CONTENT]

Add new events into the asciicast. For COMMAND help try option '-h':

COMMANDS:
    'comment' - add new line for comment effect
'commentline' - add new line for comment effect
      'clear' - command clear effect
   'duration' - set this attribute to header
       'root' - add a new prompt sequence as generic root
       'user' - add a new prompt sequence as generic root
       'line' - change event type (o|m)
     'marker' - add new event as MARKER
    'newline' - return newline effect
     'resize' - add new event as RESIZE
      'reset' - add terminal command for set to default color scheme
       'size' - set default size to header
       'time' * set same time for events scope
      'title' - set new title of asciicast

Note: If not ORDER or TIME, be event added as a last event of the record.
    * If used ORDER, be actualized event by ORDER. ORDER is not LINE!
    * If used TIME, be actualized only first event from range events with
      the same time.

    $0 set ascii.cast marker 10 'Go to logo'

Add new marker with text 'Go to logo' before event from line 10.

    $0 set ascii.cast resize -10 160x48

Add resize event after event, which started on line 10

    $0 set ascii.cast delete  -10 160x48
''' >> /dev/stderr
            ;;
        set_line)
            echo '''
    $0 event ascii.cast line 10 TYPE

Example


Value TYPE 'o' change to visible event and value 'm' change to event
marker. Next example demonstrate how-to colorize interval from 0:02 to
0:15 sec.
    $0 set ascii.cast marker 0:O2 '\u001b[1:38m'
    $0 set ascii.cast marker 0:15 '\u001b[0m'
    $0 view ascii.cast m
       8:   1: [2.0, "m", "\u001b[1;38;m"]
      ...
     150:   2: [15.0, "m", "\u001b[0m"]
    $0 set ascii.cast line 8 o
    $0 set ascii.cast line 150 o
''' >> /dev/stderr
            ;;
        set_newline) echo '''
    asciiframe set ascii.cast newline [LINE|TIME] [TYPE]

Add new line sequence, by TIME or LINE. TYPE is string, which add before
if exists.

Examples

    $ asciiframe set ascii.cast newline 0.21345

Add new line sequence, with time 0.21345

''' >> /dev/stderr
            ;;
        set_marker)
            echo """Example

    $0 event ascii.cast marker 12.25 ['# Text of the marker']

Exactly or by using of the „human” time to 12.000000:

    $0 event ascii.cast marker 0:12 ['# Example text of the marker']

How to add marker before event LINE?

    $0 event ascii.cast marker 10 'Text of marker'
""" >> /dev/stderr
            ;;
        set_resize)
            echo """
The resize event 'r' change default size terminal.

    $0 set ascii.cast resize [LINE|TIME] WIDTHxHEIGHT

Example

    $0 set ascii.cast resize 12.25 80x24

If event 'r' (resize) for time 12.250000 unexists, create new event for
change from the actually size of terminal to 80 columns and 24 rows.

    $0 set ascii.cast resize 80x24

If not TIME or LINE, add this event to end of the record and use time of
the last event for it.
""" >> /dev/stderr
            ;;
        split)

split

Tuto akci lze využít v kombinaci s akcí merge k optimalizaci asciicastu. Během záznamu asciinema neřeší sekvence, které jsou příkazem pro terminál, a tak je klidně rozdělí mezi dvě události. Během přehrávání se ty znaky pošlou na terminál tak jako tak. Jenže rozdělení takových sekvencí komplikuje střih záznamu. Proto je lepší pracovat s optimalizovaný záznamem, který lze jednoduše vyrobit následujícími příkazy:

$ asciiframe split ascii.cast > range.cast
$ asciiframe merge range.cast range > optimalized.cast

Připravte se ale na to, že zpracování záznamu, pokud byl pořízen na konzoli ve full HD rozlišení, může zabrat poměrně hodně času. Jen pro ilustraci, zpracování testovacího záznamu ascii.cast pořízeného v prostředí terminálu o velikosti do souboru rang.cast trvalo cca 2 hodiny a výsledný soubor je 14x větší

$ wc ascii.cast 
   3579    53089  1969615 ascii.cast

do

$ wc rang.cast
1129853  3899292 27750081 rang.cast

trvalo cca 2 hodiny. Chcete-li si tedy pouze zjednodušit střih, nebo zvýraznit nějakou sekvenci textu, použijte místo téhle akce příkaz split, který se volá při akci event. Ten zpracuje pouze události v úseku, kde budete chtít záznam „rozstřihnout”.

Tahle akce volá stejnou funkci jako příkaz split, jenom bez parametrů, takže se postupně zpracují události celého záznamu a výsledek se průběžně vypisuje na 'stdout, takže se dá rovnou přehrávat, ale který lze zapsat do souboru range.cast. Výsledek se během přehrávání zdá na první pohled identický, ale pokud se do něj podíváte a srovnáte obsah s originálním záznamem, zjistíte, že byly rozděleny všechny zobrazované události do jednoznakových událostí se stejným časem. A protože mají stejný čas zobrazení, zobrazují se stejně jako kdyby byly součástí jedné události, proto během přehrávání rozdíl nepoznáte.

Sloučení řetězců

split a merge. Ovšem to jsou operace, které upraví obsah původního asciicastu, kdežto akcefind'' nic nemění. Takže pokud se řetězec v záznamu nevyskytne v rámci jedné události, nevypíše nic.

            echo  """
TODO action split
    $0 play ascii.cast
""" >> /dev/stderr
            ;;
        view)

view

Umožňuje prohlížet jednotlivé události asciicastu podle času, čísla řádku a typu. Parametry zpracovává funkce get_view(), která událost obarví podle typu a předá k výpisu na stdout funkci lineblock(), která ji zalomí podle aktuální šířky terminálového okna

Obarvení událostí zajišťují funkce:

Nejsou-li předány žádné parametru, vypíšou se postupně všechny události.

Jednu konkrétní událost si lze zobrazit buď na základě času, který spadá do jejího intervalu zobrazení, nebo čísla řádku LINE na kterém se v asciicastu vyskytuje:

$ asciiframe.sh view ascii.cast 123
123: 122: [134.754764, "o", "\b\u001b[K"]

Tato akce má k dispozici také příkazy:

context
h
info
m
o
r
scope

Z výpisu jedné události si ale jen těžko uděláme představu o kontextu. Proto lze výpis rozšířit druhým parametrem CONTEXT, který zobrazí událost v širším kontextu.

Takto si vypíšeme všechny události, co se zobrazí během jedné sekundy před zobrazením události z řádku 123

$ asciiframe view ascii.cast 123 -1.0
119: 118: [133.256164, "o", "s"]
120: 119: [133.415933, "o", " "]
121: 120: [134.158507, "o", "/"]
122: 121: [134.261533, "o", "/"]
123: 122: [134.754764, "o", "\b\u001b[K"]

Čas

Když použijeme místo LINE čas TIME, bude výsledek jiný, protože na základě času se nevylistuje událost jejíž čas odpovídá předané hodnotě, ale událost, která se během přehrávání spustí jako poslední, než dojde na uvedený čas, takže i když čas předaný v následujícím ukázkovém příkladu odpovídá času události z řádku č. 123, bude zobrazena událost z řádku 122, které v ten čas skončí interval zobrazení

$ asciiframe.sh view ascii.cast 134.754764
122: 121: [134.261533, "o", "/"]

Čas lze předat také v „human-ready” formátu [HH:]MM:SS, což dává smysl pokud si chceme doplnit značky do delšího asciicastu, staženého ze serveru asciinema.org Ten nevkládá značky do uploadovaných souborů, takže je potřeba najít to správné místo podle času a dostatečně velkého časového intervalu.

scope

Pokud se v vyskytují série událostí, spouštěných ve stejný čas vylistuje jejich rozsahy. Tj. čas a pořadové číslo první a poslední řádky této série.

~$ asciiframe view ascii.cast scope [RANGE|LINE|TIME]

Rámec je sada událostí, která se zobrazí ve stejném čase. Příkaz předá funkci set_frame(), rozsah řádků a pak bude uživatel dotázán, jestli se má zapsat do asciicastu jako FRAME Pokud ne, vytiskne se obsah přes fold na stdout.

Vyhledání události přes čas zobrazení má svůj význam, také pokud si chceme zobrazit v kontextu události, které jsou v sérii událostí zobrazovaných ve stejný čas. K tomu, zda-li taková série v záznamu existuje lze využít příkaz scope

$ asciiframe.sh view ascii.cast scope
0.100000:4-5
$ asciiframe.sh view ascii.cast 0.1 -0.0001
 3:   2: [0.001000, "o", "\r\n# this is a comment; we are @ playtime 
      |       1:03 right now; this is line 2345 of recording\r\n"]
 4:  3:  [0.100000, "o", "\u001b[?2004h\u001b]0;user@stroj:
      |       ~\u0007user@stroj:~$ "]
 5:  4:  [0.100000, "o", "error "]

Události podle typu

Pamatujte, že je vždy lepší použít číslo řádku LINE. Nejenom proto, že se zpracuje nejrychleji, ale vyhnete se i riziku, že omylem zrušíte jinou událost, než jste chtěli.

Akce view je velice užitečná také při práci se značkami a protože se při jejich úpravách přes marker, nebo resize pracuje s jejich pořadím, jsou k dispozici proviewčtyři příkazy, kterými lze vypisovat tyhle události také podle pořadí ORDER, v rámci příslušného typu. Viz ukázkový příklad u příkazu line (akceset'')

            echo  '''
    asiiframe view ascii.cast LINE|TIME [LIMIT]

Show lines from ascii.cast with a order number line and order number in
scope of type. TIME, passed as float number, will be interpreted as time
than will displayed next event. LIMIT specificated by count or time show
context of event. A negative value show scope of previous events, and
positive scope of next events. For COMMAND help, add option '-h'.

    $0 view [-h] ascii.cast COMMAND [ORDER|LINE]

COMMANDS:
 context - event context of the event, the entire interval, time before
           show & time elapse to next event
    info - show info about ascii.cast file in human form
       h - show header
       o - show visible event by order
       m - show marker event by order
       r - show resize event by order
   scope - show the ranges of events, with the same time
''' >> /dev/stderr
            ;;
        view_context) echo '''
Example use: context (view)

    asciiframe view ascii.cast context LINE

Command show context event. Example of result for event from line 23:

   $ asciiframe view ascii.cast context 23
    0.673373 (0.662924 - 0.010449)
    |         |           \
    |         |            Interval to show next event from line 24
    |          \
     \          Wait elapse from show event from line 22
      INTERVAL, from previous event on 22 line to show next event from
      line 24
''' >> /dev/stderr
            ;;
        view_h) echo '''
Example use: h (view)

    asciiframe view ascii.cast h

Show header of the ascii.cast - see example of result:

  {
    "version": 2,            <- format version of the asciicast
    "width": 80,             <- original width of terminal in columns
    "height": 24,            <- original height of terminal in rows
    "timestamp": 1725352572, <- record time of the original asciicast
    "title": "Asciiframe",   <- title of record
    "duration": 9.373257,    <- the duration of the entire record
    "env": {
       "SHELL": "/bin/bash",     <- recorded shell
       "TERM": "xterm-256color"  <- colors scheme of shell
    }
  }
''' >> /dev/stderr
            ;;
        view_m) echo '''
Example use: m (view)

    asciiframe view ascii.cast m [ORDER]

Show markers events of the ascii.cast, example demonstrate ORDER using:

   $ asciiframe view ascii.cast m 2
     30:   2: [14.200000, "m", "# „On-the-fly“ translation example"]
            \
             Order marker in ascii.cast file
''' >> /dev/stderr
            ;;
        view_o) echo '''
Example use: o (view)

    asciiframe view ascii.cast o [ORDER]

Show visible events of the ascii.cast, example demonstrate ORDER using:

   $ asciiframe view ascii.cast o 14
     15:  14: [3.956926, "o", "\r\n\u001b[?2004l\r"]
            \
             Order visible event in ascii.cast file
''' >> /dev/stderr
            ;;
        view_r) echo '''
Example use: r (view)

    asciiframe view ascii.cast r [ORDER]

If used without ORDER, show all resize events in the
ascii.cast, if exists. See example of result:

   $ asciiframe view ascii.cast r
     18:   1: [5.100000, "r", "60x40"]
     21:   2: [8.100000, "r", "80x24"]
      |     \
      \      Ordering events resize type
       LINE
''' >> /dev/stderr
            ;;
        view_scope) echo '''
Example use: scope (view)

    asciiframe view ascii.cast scope [LINE|TIME|RANG]

Show rangs of events in the ascii.cast, if exists. If LINE used, show
only rang where is. See example:

   $ asciiframe view ascii.cast scope 112
   40.404066:111-121
                 \
                  Line in scope from line 111 to 121.

For TIME matter if in seconds or if „human-ready” format [HH:]MM:SS. For
„human-ready” time is found first event after - for it scope return
range, but for TIME in seconds is result only if the time is the same.

   $ asciiframe view ascii.cast scope 0:40
   40.404066:111-121
   $ asciiframe view ascii.cast scope 40.404
   $
   $ asciiframe view ascii.cast scope 40.404066
   40.404066:111-121
''' >> /dev/stderr
            ;;
            *)generic help
                echo """
Asciiframe, asciicast editing tool for the CLI, can be used to
additional post-processing the asciinema records. Distributed
by the ascii.cast – see egg for action split

    asciiframe [-d|-h|-x|-v] [-f] ACTION ascii.cast [PARAMS]

OPTIONS:
       -f  - commands realize without questions
       -h  - help for action
       -d  - verbose output (usable only for script debugging)
       -v  - print logo & version
       -x  - print history or eggs with action name

ACTIONS:
      clip - clip export and import clip
     event - add or remove events from asciicast
      find - show strings from asciicast
      play - (only X window) open a new xterm window and play ascii.cast
       set - header, size and resize and size events into asciicast
     split - optimize ascii.cast to stdout
     merge - merge ranges of events
      view - show colorized asciicast
""" >> /dev/stderr && return
            ;;
    esac
}

anone()

Jednoduchá potvrzovací funkce. Na dotaz, předaný v parametru $1 musí být jednoznačná odpověď ano-ne. V případě, že bude nastavena proměnná ${FORCE} s hodnotou yes, se automaticky vrací 0

V případě záporné odpovědi se vrací 1.

anone () {
    if [ -n "${1}" ] ; then
        if [ "${FORCE}" != "yes" ] ; then
            echo "$1" >> /dev/stderr
            while true ; do
               read -p "(ANO/NE) : " yn
                read -er yn
                case $yn in
                    [AaYyZz]*)A - Ano, A - Add, Y<=>Z - yes
                        break
                        ;;
                    [NnQqCc]*)N - no, Q - quit, C - cancel, S - storno
                        return 1
                        ;;
                    *) echo "YES or NOT?" >> /dev/stderr ;;
                esac
            done
        fi
        return 0
    else
        log warn "anone() Not value"
        return 1
    fi
}

help_line()

echo -n "╔" && echo -n $(for i in $(seq 1 78); do printf "═"; done) && echo "╗" echo -n "║" && printf "║ %.76s ║\n" && echo "║" echo -n "╚" && echo -n $(for i in $(seq 1 78); do printf "═"; done) && echo "╝" Je pomocná funkce, pro zvýraznění nápovědy

help_line () {
    printf "%0.s." $(seq 1 ${COLUMNS}) >> /dev/stderr
    return
}

asciiplayer()

Tahle funkce, kterou volá funkce get_view(), vypisuje

asciiplayer () {
    [ ${DEBUG} ] && \
        echo "INFO: asciiplayer() - ${TERMINAL}" >> /dev/stderr
    [ ! -x "${ASCIINEMA}" ] && exit 40

    local RETEZEC
    local WF
    local WH

    case ${XDG_SESSION_TYPE} in
        x11) if [ ${TERMINAL} ] ; then
               TERMINAL=rxvt asciiframe ...
                TERMINAL=${TERMINAL##*/} 
            elif [ ${RXVT} ] ; then
               rxvt
                TERMINAL=${RXVT##*/}
            elif [ ${XFCE4TERM} ] ; then
               xfce-terminal
                TERMINAL=${XFCE4TERM##*/}
            elif [ ${TERMINATOR} ] ; then
               terminator
                TERMINAL=${TERMINATOR##*/}
            elif [ ${XTERM} ] ; then
               xterm
                TERMINAL=${XTERM##*/}
            fi
            ;;
        *) TERMINAL=none
            ;;
    esac

    ROURA="${TEMP_DIR}/pipe.cast"
    [ -p ${ROURA} ] || mkfifo ${ROURA}
    if [ -z "${1}" ] ; then
        ( [ -p ${ROURA} ] && cat ${RECORDFILE} >> ${ROURA} ||  echo "ERR: asciiplayer(): FIFO ${ROURA} no exists" >> /dev/stderr &)
    elif [ -f "${1}" ] ; then
        ( [ -p ${ROURA} ] && cat ${1} >> ${ROURA} ||  echo "ERR: asciiplayer(): FIFO ${ROURA} no exists" >> /dev/stderr &)
    else
        echo "TODO: asciiplayer()" >> /dev/stderr
        return
    fi
    sleep 1
    echo "INFO: asciiplayer() ${1} ${TERMINAL} ${PLAYER} ${PLAY}" >> /dev/stderr
    (   HA=$(head -1 ${TEMP_DIR}/height)
        WA=$(head -1 ${TEMP_DIR}/width)
        case ${TERMINAL} in
                rxvt|rxvt-unicode)
                    [ ${DEBUG} ] && \
                        ${TERMINAL} -T asciinema-player -geometry "${WA}x${HA}" -c "resize ; sleep 1 ; ${ASCIINEMA} play ${ROURA}" >> /dev/stderr
                    ${TERMINAL} -T asciinema-player -geometry "${WA}x${HA}" -e bash -c "${ASCIINEMA} ${PLAY} play ${ROURA}"
                    ;;
                terminator) if [ ${WA} -gt 190 ] ; then
                       199x145 - 7.03 - 199 14.2 - 145
                        WF="7.03"
                        HF="14.2"
                    elif [ ${WA} -gt 150 ] ; then
                        WF="7.06"
                        HF="14.8"
                    else
                       80x24 – 7.095 15.0
                        WF="7.095"
                        HF="15.0"
                    fi
                    TW="$((${WA} * $(float_to_string ${WF}) / 1000000))"
                    TH="$((${HA} * $(float_to_string ${HF}) / 1000000))"
                    [ ${DEBUG} ] && \
                        echo "${TERMINAL} -T asciinema-player \
                        --geometry \"${TW}x${TH}+30+30\" \
                        -e \"resize ; sleep ; ${ASCIINEMA} play ${ROURA}\"" \
                        >> /dev/stderr
                    ${TERMINAL} -T asciinema-player \
                        --geometry "${TW}x${TH}+30+30" \
                        -e "resize ; ${ASCIINEMA} play ${ROURA}"
                    ;;
                xterm)
                    [ ${DEBUG} ] && \
                        ${TERMINAL} -T asciinema-player -geometry "${WA}x${HA}" -e "resize ; sleep 1 ; ${ASCIINEMA} play ${ROURA}" >> /dev/stderr
                    ${TERMINAL} -T asciinema-player -geometry "${WA}x${HA}" -e "${ASCIINEMA} ${PLAY} play ${ROURA}"
                    ;;
                xfce4-terminal)Pro 1600x900 je-li ${}u běžné velikosti 10x 2 14x
                    if [ ${WA} -gt "140" ] ; then
                        ZOOM="-7"
                    elif [ ${WA} -gt "150" ] ; then
                        ZOOM="-2"
                    else
                        ZOOM="0"
                    fi
                    [ ${DEBUG} ] && \
                        ${TERMINAL} -T asciinema-player --zoom ${ZOOM} --geometry "${WA}x${HA}+0+0" -e "resize ; sleep 1 ; ${ASCIINEMA} play ${PLAY} ${ROURA}" >> /dev/stderr
                    ${TERMINAL} --disable-server -T asciinema-player --zoom ${ZOOM} --geometry "${WA}x${HA}+0+0" --dynamic-title-mode=replace -e"${ASCIINEMA} play ${PLAY} ${ROURA}"
                    ;;
                *)  x=6
                    for i in $(seq 5) ; do
                        [ $x -le 1 ] && \
                            break || \
                            x=$(($x - 1))
                        printf "\rPlaying start after %d sec." "$x"
                        sleep 1
                    done
                   set default colors
                    echo -e "\x1B"$(get_string color 0)
                   H - goto top-left
                    echo -e "\x1B"$(get_string H)
                   clear screen
                    echo -e "\x1B"$(get_string J 3)
                    ${ASCIINEMA} play ${PLAY} ${ROURA}
                    return
                    ;;
            esac
    )
    return
}

events_merge()

Tato funkce, volaná příkazem text zpracovává proud událostí a slučuje jednoznakové události tak, aby vytvořily souvislé řetězece, na které lze aplikovat příkaz grep

Výstup je formátován tak, aby mohl být jednoduše rozdělen na pozici znaku 21. Pro ukázkový příklad použití viz dokumentace příkazu text

events_merge () {
    M=0
    MAX=0
    while read line ; do
        CONTENT="${line##*,\ \"}"
        ITEM="${CONTENT::-2}"
        #event_plain "${SLOUCIT}${line##*,\ \"}"
        case ${#ITEM} in
            1) MESSAGE="${MESSAGE::-2}${ITEM}\"]"
                [ ! -z ${DEBUG} ] && echo "${MESSAGE}" >> /dev/stderr
                MAX=${#MESSAGE}
            ;;
            *) if [ "${line}" == "${line/\r/}" ] ; then
                    FROM="${MESSAGE%%,*}"
                    TO="${line%%,*}"
                    if [ "${TO:0:1}" == '[' ] ; then
                        OD=$(float_to_string ${FROM:1})
                        DO=$(float_to_string ${TO:1})
                        [ "${FROM:0:1}" != '[' ] && OD="0"
                            LINE=$(get_line ${FROM:1})
                            [ -z "${LINE}" ] && LINE=1
                            TIME=$((${DO} - ${OD}))
                            [ ${TIME} -gt 1 ] && TIME=$(float_from_string ${TIME}) || TIME=$(float_from_string ${OD})
                            printf " %5s %10s : %s\n" "${LINE%%:*}" "${TIME}" "${MESSAGE}"
                    fi
                    MESSAGE="${line}"
                else
                    BEFORE="${ITEM%%\ \\r*}"
                    if [ "${ITEM#${BEFORE}\ }" == "\r" ] ; then
                        MESSAGE="${MESSAGE::-2}${BEFORE}\"]"
                    else
                        FROM="${MESSAGE%%,*}"
                        TO="${line%%,*}"
                        if [ "${TO:0:1}" == '[' ] ; then
                            OD=$(float_to_string ${FROM:1})
                            DO=$(float_to_string ${TO:1})
                            [ "${FROM:0:1}" != '[' ] && OD="0"
                            LINE=$(get_line ${FROM:1})
                            TIME=$((${DO} - ${OD}))
                            [ ${TIME} -gt 1 ] && TIME=$(float_from_string ${TIME}) || TIME='1.0'
                            printf " %5s %10s : %s\n" "${LINE%%:*}" "${TIME}" "${MESSAGE}"
                        fi
                        MESSAGE="${line}"
                    fi
                fi
            ;;
        esac
    done
}

event_decolor()

Zpracuje předaný rozsah tak, že volá funkci sm(), která rozděluje událost na jednoznakové události, které odbarvuje přes funkci event_plain() a sestavuje zpět pomocí ???

event_decolor () {
    [ ${DEBUG} ] && \
        echo "INFO: event_decolor() - LINE ${LINE} ENDLINE ${ENDLINE} ${TIME}" >> /dev/stderr

  echo "decolorize rozsah událostí"
    echo TODO-event-decolor
  echo "Volaná funkce, zpracuje rozsah $LINE $ENDLINE tak, že volá funkci sm(), která události rozkládá na znaky, z výsledné série událostí odfiltruje vše co se týče obarvení textu a sekvence pomocí
  merge opět sloučí" >> /dev/stderr
  echo "TODO split & merge!" >> /dev/stderr
  return

    if [ -z "${ENDLINE}" ] ; then
        for i in $(sm $(sed -n ${LINE}p ${RECORDFILE}) |\
            sed 's/\\u001b\[[0-9;]\+m//g') ; do
            case ${i} in
                '˙') STRING="${STRING}." ;;
                ' ') STRING="${STRING} " ;;
                *) STRING="${STRING}${i}" ;;
            esac
        done
       tady už se pracuje se zpracovanou událostí
        echo ${STRING}
        return
        [ ${DEBUG} ] && \
            echo "INFO: same_time() ${1} line ${LINE}" >> /dev/stderr
        (
                ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
        ) >> ${TEMP_DIR}/same_event
       jiná událost, jiný soubor
        EVENT=$(head -1 ${TEMP_DIR}/same_event)
        echo ${EVENT} >> /dev/stderr
        anone "Change time of this event from line ${LINE} to ${TIME}? For continue push 'y' key"
        [ $? -eq 1 ] && \
            return
       change time
        ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
.s/[^,]*\,/[${TIME},/1
w
q
EOF
    else
        [ ${DEBUG} ] && \
            echo "INFO: same_time() ${1} by scope from ${LINE} line to ${ENDLINE}" >> /dev/stderr
        [ ${ENDLINE} -lt $((${LINE} + 1)) ] && \
            echo "ERROR: same_time() - Invalid scope for ${1} (${LINE} - ${ENDLINE})" >> /dev/stderr && \
            return 1
       tady se zpracují události jedna po druhé..
        return
        sed -n ${LINE},${ENDLINE}p ${RECORDFILE}
        anone "Set for this scope ${LINE}-${ENDLINE} time to ${TIME}? For continue push 'y' key." >> /dev/stderr
        [ $? -eq 1 ] && \
            return
        ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE},${ENDLINE}s/[^,]*\,/[${TIME},/1
w
q
EOF
    fi
    return
}

event_plain()

Funkce z řetězce události $1 odstraňuje barvy a jiné zvýraznění textu. Problém je, pokud je řetězec, kterým se nastavuje zvýraznění rozdělen přes dvě události, např.

  ... \u001b"]
[0.403087, "o", "[38;5;76m "]

kde řetězec zobrazované události končí znakem \u0001b, po kterém by měly následovat další znaky, které jsou již součástí následující událost. Nebo pokud řetězec zobrazované události končí neukončenou sekvencí pro nastavení barvy

  ... \u001b[38;5;"]
[0.43567, "o", "66m ...

Řešením je pouze sloučení událostí, jako to dělá funkce print_monochrome() a opětové zavolání funkce event_plain()

event_plain () {
    sed 's/\\u001b\[[0-9;]\+m//g' <<< "${1}"
    return
}

get_resize()

Tato funkce přijme jako parametr $1 číslo řádky na které se vyskytuje událost typu 'r', obsahující informaci o změně standarní velikosti terminálu. Zracuje její obsah a vrátí ve formě třech řetězců: TIME, WSIZE a HSIZE

get_resize () {
    [ ${DEBUG} ] && \
        echo "INFO: get_resize() – ${1}" >> /dev/stderr
    local CONTENT
    local HEIGHT
    local LINE
    local WIDTH

    LINE=$(sed -n ${1}p ${RECORDFILE} | grep '"r"')
    CONTENT="${LINE#*\[}"
    #/ echo "${CONTENT}" >> /dev/stderr
    WIDTH="${CONTENT%x*}"
    HEIGHT="${CONTENT##*x}"
    printf "%s %s %s" $(float_to_string ${CONTENT%%,*}) "${WIDTH##*\"}" "${HEIGHT%\"*}"
    return
}

header_print()

Funkce sestaví hlavičku pro nový klip

header_print () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME()" >> /dev/stderr

        printf "{\"version\": %d," $(head -1 ${TEMP_DIR}/version)
        printf " \"width\": %d," $(head -1 ${TEMP_DIR}/width)
        printf " \"height\": %d," $(head -1 ${TEMP_DIR}/height)
        printf " \"timestamp\": %d," $(head -1 ${TEMP_DIR}/timestamp)
        TEST=$(head -1 ${TEMP_DIR}/title)
        [ "${TEST}" != null ] && \
            printf " \"title\": \"$(head -1 ${TEMP_DIR}/title)\","
        TEST=$(head -1 ${TEMP_DIR}/duration)
        if [ -z "${TEST}" ] || [ "${TEST}" == "null" ] ; then
            echo > /dev/null
        else
            printf " \"duration\": ${TEST},"
        fi
        TEST=$(head -1 ${TEMP_DIR}/idle)
        if [ -z "${TEST}" ] || [ "${TEST}" == "null" ] ; then
            echo > /dev/null
        else
            printf " \"idle_time_limit\": ${TEST},"
        fi
      if [ ! -z ${FG} ] || \
          [ ! -z ${BG} ] || \
          [ ! -z ${PALLETE} ] ; then
              echo theme >> /dev/null
              #ORIGFG
              #ORIGBG
              #ORIGPALLETE
      fi

        printf " \"env\": {"
        printf "\"SHELL\": \"$(head -1 ${TEMP_DIR}/shell)\","
        printf " \"TERM\": \"$(head -1 ${TEMP_DIR}/term)\"}"
        echo "}"
    return
}

include_clip()

Tahle funkce začleňuje do asciicastu klip, nebo jiný asciicastový soubor, pokud ho dostane jako parametr $3.

V případě, že parametr $3 chybí, nebo funkce value() vrátí jinou hodnotu než file, dojde pouze na úpravu časování stávajících událostí dle v parametru $2, a to od události, která následuje po čase, co byl v parametru $1 – oba předané parametry tedy musí být typu float (číslo s tečkou).

$1 - čas vložení

Bude-li mít parametr $1 hodnotu 0.0, vloží se události klipu před stávající obsah asciicastu. Je-li předán jiný čas, vypíšou se všechny události až do onoho času, aniž by se měnilo jejich časování, a to bez ohledu na to bude-li se vkládat nějaký klip (či jiný asciicastový soubor), nebo ne.

Při vložení se z hlavičky klipu (resp. jiného asciicastu) vygeneruje událost typu m (marker), jejíž obsah bude vycházet z atributu 'title' – bude-li existovat. Nebude-li existovat, použije se místo něj jméno asciicastového souboru a jeho časové razítko. A za ní, pokud má klip v hlavičce jinou velikost terminálu než záznam, do kterého se klip vkládá bude následovat událost typu r resize.

$2 - posun časování

Parametr $2, který může mít hodnotu kladnou i zápornou, nastavuje časový interval o který se má posunout výchozí časování klipu. Bude-li mít zápornou hodnotu, může být dlouhý maximálně tak, jak je dlouhý interval zobrazení předchozí události.

Časování událostí, které se posunou až za poslední událost vloženého klipu, se upraví podle toho, jak vyjde časování poslední události vloženého klipu.

Čerstvě pořízený asciicast obvykle zobrazením poslední události přehrávání ukončí, takže jeho celkový čas odpovídá času, jejího zobrazení, ale při exportu klipu do hlavičky generuje funkce clip_print() také atribut 'duration', jehož hodnota se při vložení natáhne do proměnné ${CLIPDURATION} – ta určí, jak dlouhý bude interval zobrazení za poslední událostí klipu.

U čerstvě pořízeného asciicastu ale atribut 'duration' v hlavičce chybí, proto se časování událostí za vloženým souborem automaticky posouvá o stejný interval, jaký události předcházel před vložením klipu. A to lze pomocí parametru $2 měnit.

Když parametr $3 chybí…

V případě, že se žádný jiný asciicast, nebo klip nevkládá, lze pomocí této funkce upravit stávající časování událostí.

To se hodí například pokud chceme přes akci event do záznamu doplnit komentář a následně posunout časy zobrazení událostí tak, aby se dal přečíst, aniž by bylo nutné změnit rychlost přehrávání nebo ho pozastavit.

Záporná hodnota parametru $2 naopak pomůže, pokud během nahrávání asciicastu bylo nutné udělat i jiné, neplánované operace. Ty se dají „vystřihnout” přes akci event příkazem 'delete' a takto vytvořená prodleva se dá redukovat právě pomocí akce clip. To jakou hodnotu pro $2 lze použít, se dá nejlépe zjistit pomocí akce view.

include_clip () {
    echo "INFO: include_clip() START ${1} SHIFT ${2} CLIP ${3}" >> /dev/stderr

    local BEFORE
    local CLIP
    local LINE
    local END
    local ENDLINE
    local START
    local TEST
    local SHIFT
    local TIME

   
    if [ -f ${3} ] ; then
        CLIP=${3}
    fi

   
    case $(value ${2}) in
        flow) SHIFT=$(float_to_string ${2})
            ;;
        nflo) SHIFT="-$(float_to_string ${2:1})"
            ;;
        *) echo "INFO: As shift time must be used float number as -0.123 or 0.123" >> /dev/stderr
        ;;
    esac

    case $(value ${1}) in
        flow|time) if [ ${1/:/} == ${1} ] ; then
                TIME=${1}
            else
                TIME=$(print_seconds ${1})
            fi
            LINE=$(line_by_time ${TIME})
            if [ $? -eq 1 ] ; then
                LINE="${#LINES[@]}"
                echo "INFO: include_clip() - add clip to TIME ${TIME} as LINE ${LINE}" >> /dev/stderr
            else
                TEST=$(get_view context ${LINE})
                TEST=${TEST#*\(}
                BEFORE=$(float_to_string ${TEST%% *})
            fi
        ;;
        inte) if [ "${1}" -eq "1" ] || [ "${1}" -eq "2" ]  ; then
                LINE="2"
                TEST=$(get_view context ${LINE})
                TEST=${TEST#*\(}
                BEFORE=$(float_to_string ${TEST%% *})
                echo "INFO: include_clip() - before first event to LINE ${LINE}, use TIME ${TIME} and default SHIFT ${SHIFT}" >> /dev/stderr
            elif [ "${1}" -le  $((${#LINES[@]} +1 )) ] ; then
                LINE="${1}"
                TEST=$(get_view context ${LINE})
                TEST=${TEST#*\(}
                BEFORE=$(float_to_string ${TEST%% *})
                if [ $((${BEFORE} + ${SHIFT})) -ge 0 ] ; then
                    echo "INFO: include_clip() - add clip as line ${LINE} TIME ${TIME} with default SHIFT" >> /dev/stderr
                else
                    echo "ERROR: include_clip - max negative SHIFT value: -$(float_from_string ${BEFORE})" >> /dev/stderr
                    return 1
                fi
            else
                echo "ERROR: include_clip - Out of rang parameter ${#LINES[@]} ${1}" >> /dev/stderr
                return 1
            fi
        ;;
        stri) echo "Příkaz diff"
            return
        ;;
    esac

    if [ -f "${CLIP}" ] ; then
        CLIPTEST=$(head -1 ${CLIP})
        if [ ${#CLIPTEST} -eq 1 ] ; then
         # struct JSOM
            CLIPFILE="${TEMP_DIR}/json.clip"
            ${JQ} . -c -M ${3} > ${CLIPFILE}
            CLIPHEADER=$(head -1 ${CLIPFILE})
        elif [ ${#CLIPTEST} -eq 8 ] ; then
           struct colorized JSON
            CLIPFILE="${TEMP_DIR}/json.clip"
            cat ${CLIP} | decolor | ${JQ} . -c -M > ${CLIPFILE}
        else
            CLIPFILE=${CLIP}
        fi
        clip_load
        [ -z ${CLIPLINES} ] && return 1
        echo ${CLIPLINES[@]} >> /dev/stderr

        if [ -z "${TIME}" ] ; then
            START=$(float_from_string ${LINES[$((${LINE} - 2))]:1})
            (
            printf '[%s, "m", "%s"]\n' "${START}" "$(head -1 ${TEMP_DIR}/ctitle) - $(head -1 ${TEMP_DIR}/ctimestamp)"
            printf '[%s, "r", "%s"]\n' "${START}" "$(head -1 ${TEMP_DIR}/cwidth)x$(head -1 ${TEMP_DIR}/cheight)"
            ) > ${TEMP_DIR}/clip.marker
            echo null > ${TEMP_DIR}/duration
        else
            (
            printf '[%s, "m", "%s"]\n' "${TIME}" "$(head -1 ${TEMP_DIR}/ctitle) - $(head -1 ${TEMP_DIR}/ctimestamp)"
            printf '[%s, "r", "%s"]\n' "${TIME}" "$(head -1 ${TEMP_DIR}/cwidth)x$(head -1 ${TEMP_DIR}/cheight)"
            ) > ${TEMP_DIR}/clip.marker
            echo null > ${TEMP_DIR}/duration
        fi
       new timestamp
        echo $(date +%s) > ${TEMP_DIR}/timestamp
        header_print
        x=0
        ${SED} 's/\\/\\\\/g' ${RECORDFILE} | while read line ; do
            if [ ${x} -eq $((${LINE} - 1)) ] ; then
                if [ ${LINE} -le 2 ] ; then
                   to front
                    [ ${START} ] && POSUN=$(float_to_string ${START}) || POSUN=$(float_to_string ${TIME})
                    echo "---- VVV ${CLIPFILE} add before first event of the original ascii.cast VVV ----" >> /dev/stderr
                   add new marker
                    cat ${TEMP_DIR}/clip.marker
                    y=0
                    TEST=0
                    ${SED} 's/\\/\\\\/g' ${CLIPFILE} | while read clipline ; do
                        if [ $y -eq 0 ] ; then
                            [ ${DEBUG} ] && \
                                echo "CLIP HEAD ${line}" >> /dev/stderr
                        elif [ $y -ge 1 ] ; then
                            TEST=$((${CLIPLINES[$(($y - 1))]:1} + ${POSUN}))
                            echo ${TEST} > ${TEMP_DIR}/last_time
                            echo ${clipline} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${TEST})/"
                        fi
                        y=$(($y + 1))
                    done
                    echo "---- AAA ${CLIPFILE} add before first event of the original ascii.cast AAA ----" >> /dev/stderr
                   shift time the first event of the original ascii.cast
                    POSUN=$(($(head -1 ${TEMP_DIR}/last_time) - ${POSUN} + ${BEFORE} + ${SHIFT}))
                    [ $(head -1 ${TEMP_DIR}/last_time) -gt ${POSUN} ] && POSUN=$(head -1 ${TEMP_DIR}/last_time)
                    echo ${line} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${POSUN})/"
                elif [ ${LINE} -eq $((${#LINES[@]} + 1)) ] ; then
                   Add after last event of the original ascii.cast
                    echo ${line}
                    [ ${START} ] && POSUN=$(float_to_string ${START}) || POSUN=$(float_to_string ${TIME})
                    echo "---- VVV ${CLIPFILE} append after last event of the original ascii.cast VVV ----" >> /dev/stderr
                   add new marker
                    cat ${TEMP_DIR}/clip.marker
                    y=0
                    TEST=0
                    ${SED} 's/\\/\\\\/g' ${CLIPFILE} | while read clipline ; do
                        if [ $y -eq 0 ] ; then
                            [ ${DEBUG} ] && \
                                echo "CLIP HEAD ${line}" >> /dev/stderr
                        elif [ $y -ge 1 ] ; then
                            TEST=$((${CLIPLINES[$(($y - 1))]:1} + ${POSUN}))
                            echo ${TEST} > ${TEMP_DIR}/last_time
                            echo ${clipline} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${TEST})/"
                        fi
                        y=$(($y + 1))
                    done
                    POSUN=$(($(head -1 ${TEMP_DIR}/last_time) - ${POSUN} + ${BEFORE} + ${SHIFT}))
                    echo "---- AAA ${CLIPFILE} append after last event of the original ascii.cast AAA ----" >> /dev/stderr
                else
                   to middle
                    [ ${START} ] && POSUN=$(float_to_string ${START}) || POSUN=$(float_to_string ${TIME})
                    echo "---- VVV Include ${CLIPFILE} to LINE ${LINE} with TIME ${TIME} VVV ----" >> /dev/stderr
                   add new marker
                    cat ${TEMP_DIR}/clip.marker
                    y=0
                    TEST=0
                    ${SED} 's/\\/\\\\/g' ${CLIPFILE} | while read clipline ; do
                        if [ $y -eq 0 ] ; then
                            [ ${DEBUG} ] && \
                                echo "CLIP HEAD ${line}" >> /dev/stderr
                        elif [ $y -ge 1 ] ; then
                            TEST=$((${CLIPLINES[$(($y - 1))]:1} + ${POSUN}))
                            echo ${TEST} > ${TEMP_DIR}/last_time
                            echo ${clipline} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${TEST})/"
                        fi
                        y=$(($y + 1))
                    done
                    echo "---- AAA Include ${CLIPFILE} to LINE ${LINE} with TIME ${TIME} AAA ----" >> /dev/stderr
                    POSUN=$(($(head -1 ${TEMP_DIR}/last_time) - ${LINES[$(($x - 1))]:1} + ${BEFORE} + ${SHIFT}))
                    TIME=$((${LINES[$(($x - 1))]:1} + ${POSUN}))
                    [ ${TIME} -lt $(head -1 ${TEMP_DIR}/last_time) ] && \
                        POSUN=$(($(head -1 ${TEMP_DIR}/last_time) - ${LINES[$(($x - 1))]:1})) && \
                            TIME=$(head -1 ${TEMP_DIR}/last_time)
                    echo ${line} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${TIME})/"
                fi
            elif [ ${x} -gt $((${LINE} - 1)) ] ; then
               echo vkládám zbytek původního ascii.cast souboru
                TIME=$((${LINES[$(($x - 1))]:1} + ${POSUN}))
                echo ${line} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${TIME})/"
            elif [ ${x} -eq 0 ] ; then
               only for debug info
                [ ${DEBUG} ] && \
                    echo "ORIGINAL HEAD ${line}" >> /dev/stderr
            else
               don't touch time of event
                echo ${line}
            fi
            x=$(($x + 1))
        done
    else
       new duration
        float_from_string $((${LINES[-1]:1} + ${SHIFT} )) > ${TEMP_DIR}/duration
       new timestamp
        echo $(date +%s) > ${TEMP_DIR}/timestamp
        header_print
        x=0
        ${SED} 's/\\/\\\\/g' ${RECORDFILE} | while read line ; do
             if [ ${x} -ge $((${LINE} - 1)) ] ; then
                TIME=$((${LINES[$(($x - 1))]:1} + ${SHIFT}))
                echo ${line} | ${SED} "s/:digit:\+.:digit:\+/$(float_from_string ${TIME})/"
            elif [ ${x} -eq 0 ] ; then
               only for debug info
                [ ${DEBUG} ] && \
                    echo "ORIGINAL HEAD ${line}" >> /dev/stderr
            else
               don't touch time of event
                echo ${line}
            fi
            x=$(($x + 1))
        done
        if [ ${SHIFT/-/} == ${SHIFT} ] ; then
            echo "INFO: include_clip() - Waiting to show event from line ${LINE} was extended. Next events time was shifted by $(float_from_string ${SHIFT}) seconds forward." >> /dev/stderr
        else
            echo "INFO: include_clip() - The waiting interval to show event from line ${LINE} has been shortened. The next events timing has been shifted by -$(float_from_string ${SHIFT:1}) seconds to back." >> /dev/stderr
        fi
    fi

    return
}

clip_print()

Tahle funkce exportuje klipy, které se dají samostatně přehrávat, protože jsou také v asciicast formátu, jako výchozí záznam.

Akceptuje tyto parametry:

$1 je číslo řádku, nebo čas od kterého se mají události exportovat.
$2 určuje událost, kterou bude klip končit. Může to být počet řádků, časový interval v sekundách, ale i ve formátu HH:MM:SS. Záporné hodnoty se interpretují vůči poslední události záznamu, takže..

$ asciiframe clip ascii.cast 1:00 -0.5

Výše uvedený příkaz vyexportuje do klipu veškeré události od času 60.000000 sec. až před poslední půlsekundu před koncem záznamu.

Proto, chcete-li vymezit časový interval, můžete použí čas ve formátu HH:MM:SS. Viz následující příklad, kterým se doklipu vyexportují události od času 60.000000 sec. po čas 90.0000000 sec. od startu:

$ asciiframe clip ascii.cast 1:00 1:30

Akci clip lze využít i přehrání učitého časového úseku záznamu. Aplikace asciinema totiž sama o sobě přehrávat záznam od určitého času neumí. Následující ukázka demonstruje jak na to.

1. možnost je vyexportovat úsek, který nás zajímá do souboru clip.cast. Přesměrování je dležité, protože bez něj bychom ztratili přehled o kontextu předchozích událostí. Ty se totiž posílají při exportu klipu do /dev/null resp. na stderr s časem spuštění 0.0

$ asciiframe clip ascii.cast 1.23 1:00 > clip.cast 2>&1 $ asciinema play clip.cast

2. možností, kterou máte je přesměrování výstupu rovnou do přehrávače asciinema, jenže ten má poměrně omezné možnosti. $ asciiframe clip ascii.cast 1.23 1:00 > /dev/stdout 2>&1 | asciinema play -

3. proto mají uživatelé linuxových desktopů k dispozici ještě třetí možnost – přesměrování na přehráč spuštěný přes asciiframe:

$ asciiframe clip ascii.cast 1.23 1:00 play 0:05

Čas uvedený za příkazem play je velikost pauzy, mezi zobrazením předcházejících zpráv a a událostmi klipu.

Ǔčelem této pauzay je identifikace událostí, které mají být součástí úvodního „screenshotu” – pokud ho budete chtít udělat, aplikujte na příslušný úsek nejprve akci split a když zjistíte kterou událostí musí „screenshot” aplikujte akci merge s číslem řádku události, kterou screenshot začíná a číslem řádky události, která ho ukončí.

Důležitá poznámka

Akce merge operuje přímo s obsahem výchozího souboru, zatímco coclip exportuje události na stdout, který lze přesměrovat do samostatného klipu a ten naimportovat až po případných úpravách, což výrazně snižuje riziko poškození výchozího souboru.

TODO - je třeba ošetřit parametr ${2} (INTERVAL), který může být předán ve formátu HH:MM:SS, nebo jako kladné či záporné číslo v sekundách

clip_print () {
    [ ${DEBUG} ] && \
        echo "INFO: clip_print() – $1 ; $2 ; $3" >> /dev/stderr
    local LINE
    local END
    local ENDLINE
    local START
    local TIME

    case $(value ${1}) in
        flow) START=$(line_by_time ${1})
            [ $? -eq 1 ] && \
                echo "ERROR: clip_print() - Time ${1} is out of the rang events in ${RECORDFILE}" >> /dev/stderr && \
                return 1
            [ ${DEBUG} ] && \
                echo "Pro události do času $1 se nastaví čas 0" >> dev/stderr
        ;;
        inte) LINE=$(line_by_inte ${1})
            [ $? -eq 1 ] && \
                echo "ERROR: clip_print() - Value ${1} is out of the rang events of ${RECORDFILE}" >> /dev/stderr && \
                return 1
            START=$((${LINE} - 1))
            [ ${DEBUG} ] && \
                echo "Pro události do řádku ${START} se nastaví čas 0.0" >> /dev/stderr
        ;;
        rang) TEST=(${1/-/ })
            LINE=$(line_by_inte ${TEST[0]})
            [ $? -eq 1 ] && \
                echo ${LINE} && \
                    return 1
            ENDLINE=$(line_by_inte ${TEST[1]})
                [ $? -eq 1 ] && \
                    echo ${ENDLINE} && \
                        return 1
            [ ${DEBUG} ] && \
                echo "INFO: do_event() ${1} by scope from ${LINE} to ${ENDLINE}" >> /dev/stderr
            START=$((${LINE} - 1))
            END=${ENDLINE}
        ;;
        time) START=$(line_by_time $(print_seconds ${1}))
            [ $? -eq 1 ] && \
                echo "ERROR: clip_print() - Time ${1} is out of the rang events in ${RECORDFILE}" >> /dev/stderr && \
                return 1
            [ ${DEBUG} ] && \
            echo "Pro události do času $1 se nastaví čas 0" >> dev/stderr
        ;;
    esac
    SHIFT=$(( $(time_by_line ${START}) - 1100000 ))
    OUT="/dev/stdout"
    PAUSE=1
    echo "Parameter ${2} SHIFT ${SHIFT}" >> /dev/stderr
    if [ ${2} ] ; then
        case $(value ${2}) in
            flow) [ ${DEBUG} ] && \
                    echo "INFO: clip_print() - End of rang is set by shift time ${2} from actually time of line ${START}" >> dev/stderr
            ;;
            inte) [ ${DEBUG} ] && \
                    echo "INFO: clip_print() - End of rang is set by count of lines from actually line ${START}" >> /dev/stderr
            ;;
            nint) [ ${DEBUG} ] && \
                    echo "INFO: clip_print() - End of rang is set by count of lines (${2}) from end of file" >> /dev/stderr
            ;;
            nflo) [ ${DEBUG} ] && \
                    echo "INFO: clip_print() - End of rang is set by time end ${2} seconds before end of file" >> /dev/stderr
            ;;
            stri) case ${2} in
                    play) [ ${DEBUG} ] && \
                            echo "INFO: clip_print() - Play from START line ${START} to end of file" >> /dev/stderr
                        PLAY=yes
                        [ -z ${3} ] && PAUSE=3000000 || PAUSE=$(print_seconds ${3})
                        OUT=${TEMP_DIR}/clip.cast
                        ;;
                    clip) CLIP=yes
                        ;;
                esac
            ;;
        esac
    fi
    [ -z ${END} ] && \
        END=$(( ${#LINES[@]} + 1 ))
    header_print >> ${OUT}
    x=0
    cat ${RECORDFILE} | sed 's/\\/\\\\/g' | while read line ; do
        ((x++))
        #echo ${x} >> /dev/stderr
        if [ ${x} -eq 1 ] ; then
            echo "${line}" >> /dev/null
        elif [ ${x} -le ${START} ] ; then
            #echo "sypu jeden čas" >> /dev/stderr
            [ ${CLIP} ] && echo ${line} >> /dev/null || echo ${line} | sed 's/^\[[0-9.]\+,/\[0.0,/' >> ${OUT}
        elif [ ${x} -gt ${END} ] ; then
            echo "sypu do" >> /dev/null
        else
            #echo "recount čas" >> /dev/stderr
            TIME=$(float_strip $(float_from_string $((${LINES[$(($x-2))]:1} - ${SHIFT} + ${PAUSE}))))
            #echo "${line}" >> /dev/stderr
            echo ${line} | sed 's/^\[[0-9.]\+,/\['${TIME}',/' >> ${OUT}
        fi
    done
    echo "INFO: clip_print() - Clip from START ${START} to END ${END} used SHIFT ${SHIFT}" >> /dev/stderr
    [ ${PLAY} ] && asciiframe play ${OUT}
    return
}

context_show()

Funkce zobrazuje kontext jedné události z řádku $1 Při zpracování volá pomocné funkce:

float_from_string()
get_context()
line_by_inte()

context_show () {
    [ ${DEBUG} ] && \
        TEST=$(line_by_inte "${1}") || \
        TEST=$(line_by_inte "${1}" 2>/dev/null)
    [ $? -eq 1 ] && \
        echo "ERROR: $FUNCNAME() - line ${1} is out of events the ${RECORDFILE} file" >> /dev/stderr && \
            return 1
    [ ${TEST} -eq 1 ] && \
        echo "ERROR: $FUNCNAME() - line ${TEST} is the header of the ${RECORDFILE} file" >> /dev/stderr && \
        return 1
    CONTEXT=$(get_context ${TEST})
    [ -z ${CONTEXT} ] && \
        echo "ERROR: $FUNCNAME() - No context for ${1}. Probably is out of rang events from ${RECORDFILE}" >> /dev/stderr && \
        return 1
    EVENT=(${CONTEXT//\;/ })
    PREV=(${EVENT[0]//\:/ })
    ITEM=(${EVENT[1]//\:/ })
    NEXT=(${EVENT[2]//\:/ })
    [ ${DEBUG} ] && \
        echo "INFO: $FUNCNAME() 'context': PREV ${PREV[@]}" >> /dev/stderr
    [ ${DEBUG} ] && \
        echo "INFO: $FUNCNAME() 'context': ITEM ${ITEM[@]}" >> /dev/stderr
    [ ${DEBUG} ] && \
        echo "INFO: $FUNCNAME() 'context': NEXT ${NEXT[@]}" >> /dev/stderr
    ITEM[2]=$((${ITEM[1]} - ${PREV[1]}))
    #echo ${ITEM[2]} >> /dev/stderr
    if [ ${ITEM[2]} -eq ${ITEM[1]} ] ; then
        [ ${DEBUG} ] && \
            echo "INFO: $FUNCNAME() 'context' - Event from line ${NEXT[0]} and ${ITEM[0]} showed in same time" >> /dev/stderr
    elif [ ${ITEM[2]} -lt 1 ] ; then
        [ ${DEBUG} ] && \
            echo "WARN: $FUNCNAME() 'context' - Line ${NEXT[0]}: $(float_from_string ${NEXT[1]}) started before then event from line ${ITEM[0]}: $(float_from_string ${ITEM[1]})" >> /dev/stderr
    fi
    if [ ${NEXT[0]} -eq 0 ] ; then
        printf "%s (%s - %s)\n" \
            "$(float_from_string ${ITEM[2]})" \
            "$(float_from_string ${ITEM[2]})" \
            "$(float_from_string 0)"
        return
    else
        if [ ${NEXT[1]} -gt 0 ] ; then
            NEXT[2]=$((${NEXT[1]} - ${ITEM[1]}))
            if [ ${NEXT[2]} -lt 0 ] ; then
                echo "WARN: $FUNCNAME() 'context': Line ${NEXT[0]}: $(float_from_string ${NEXT[1]}) started before then event from line ${ITEM[0]}: $(float_from_string ${ITEM[1]})" >> /dev/stderr
                return 1
            else
                printf "%s (%s - %s)\n" \
                    "$(float_from_string $((${ITEM[2]} + ${NEXT[2]})))" \
                    "$(float_from_string ${ITEM[2]})" \
                    "$(float_from_string ${NEXT[2]})"
            fi
        else
           předposlední event
            PREV[2]=$((${ITEM[1]} - ${PREV[1]}))
            if [ ${PREV[2]} -lt 0 ] ; then
                echo "WARN: $FUNCNAME() 'context': Line ${ITEM[0]}: $(float_from_string ${ITEM[1]}) started before then event from line ${PREV[0]}: $(float_from_string ${PREV[1]})" >> /dev/stderr
                return 1
            else
                printf "%s (%s - %s)\n" \
                    "$(float_from_string ${ITEM[2]})" \
                    "$(float_from_string ${ITEM[2]})" \
                    "$(float_from_string 0)"
            fi
        fi
    fi
    return
}

get_context()

Funkce vrací kontext jedné události na řádku $1

get_context () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - ${1} - ${2}" >> /dev/stderr
    local BEFORELINE
    local BEFORETIME
    local NEXTLINE
    local NEXTTIME
    local LINE
    local TIME
    local TEST

    case $(value ${1}) in
        flow|nflo|time) TEST=$(line_by_time ${1})
            [ $? -eq 1 ] && \
                echo "ERROR: $FUNCNAME() - Time ${1} out of rang events ${RECORDFILE}" >> /dev/stderr && \
                return 1
        ;;
        inte|nint) TEST=$(line_by_inte ${1})
            [ $? -eq 1 ] && \
                echo "ERROR: $FUNCNAME() - Line ${1} out of rang events ${RECORDFILE}" >> /dev/stderr && \
                return 1
        ;;
    esac
    if [ ${TEST} -eq 1 ] ; then
        BEFORELINE=0
        BEFORETIME=0
        LINE=1
        TIME=0
        NEXTLINE=2
        NEXTTIME=${LINES[0]:1}
    elif [ ${TEST} -eq 2 ] ; then
        BEFORELINE=1
        BEFORETIME=0
        LINE=2
        TIME=${LINES[0]:1}
        NEXTLINE=3
        NEXTTIME=${LINES[1]:1}
    else
        LINE=${TEST}
        TIME=${LINES[$((${TEST}-2))]:1}
        BEFORELINE=$((${TEST}-1))
        BEFORETIME=${LINES[$((${TEST}-3))]:1}
        NEXTLINE=$((${TEST}+1))
        NEXTTIME=${LINES[$((${TEST}-1))]:1}
        [ ${DEBUG} ] && \
            echo "DEBUG: $FUNCNAME() LINE ${LINE} TIME ${TIME}" >> /dev/stderr
        [ ${DEBUG} ] && \
            echo "DEBUG: $FUNCNAME() BEFORELINE ${BEFORELINE} BEFORETIME ${BEFORETIME}" >> /dev/stderr
        [ ${DEBUG} ] && \
            echo "DEBUG: $FUNCNAME() NEXTLINE ${NEXTLINE} NEXTTIME ${NEXTTIME}" >> /dev/stderr
        if [ -z ${NEXTTIME} ] ; then
            NEXTLINE=0
            NEXTTIME=0
        fi
    fi
    [ ${DEBUG} ] && \
        echo "RETURN: $FUNCNAME() - $BEFORELINE:$BEFORETIME;$LINE:$TIME;${NEXTLINE}:${NEXTTIME}" >> /dev/stderr
    echo "$BEFORELINE:$BEFORETIME;$LINE:${TIME};${NEXTLINE}:${NEXTTIME}"
    return
}

float_from_string()

Funkce převádí textový řetězec, předaný v parametru $1 na číslo typu float. Tedy číslo, které používá jako desetinnou čárku tečku.

Předpokládá, že jde o kladné celé číslo typu integer, co udává délku časového intervalu v mikrosekundách. Je-li předaná hodnota 0, vrátí se řetězec 0.000000

Kdybychom to počítali přec bc, trvalo by zpracování jednoho čísla 11-14 tisícin sekundy, kdežto této funkci to trvá jen 5-6 tisícin sekundy

float_from_string () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - ${1}" >> /dev/stderr
    local FLOAT
    local LENGTH
    local STRING

    STRING=${1}
    case $(value ${STRING}) in
        inte)
        ;;
        *) echo "ERROR: float_from_string() not accept $(value ${1}) ${1}" >> /dev/stderr
            return 1
        ;;
    esac
    LENGTH=${#STRING}
    case ${#STRING} in
        1) FLOAT="0.00000${STRING}";;
        2) FLOAT="0.0000${STRING}" ;;
        3) FLOAT="0.000${STRING}" ;;
        4) FLOAT="0.00${STRING}" ;;
        5) FLOAT="0.0${STRING}" ;;
        6) FLOAT="0.${STRING}" ;;
        *) FLOAT="${STRING:0:$(( ${LENGTH} - 6 ))}.${STRING:$(( ${LENGTH} - 6 )):6}"
        ;;
    esac
    [ ${DEBUG} ] && \
        echo "RETURN: $FUNCNAME()- FLOAT ${FLOAT} (send to strip)" >> /dev/stderr
    echo $(float_strip ${FLOAT})
    return
}

float_to_string()

Funkce vychází z toho, že textový řetězec, předaný v parametru $1 je číslo typu float. Tedy číslo, které používá jako desetinnou čárku tečku. Za ní může následovat blíže neznámý počet desetiných míst, ale během přehrávání asciicastového záznamu nehraje roli je-li interval mezi událostmi kratší než 1 mikrosekunda – proto stačí zpracovat jen následujících 6 desetiných míst.

Maximální počet mikrosekund limitován není, ale délka vašeho záznamu pravděpodobně nikdy nebude větší než 9999999999 mikrosekund, tedy 2 hodiny 46 minut a 40 sekund.

float_to_string () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - ${1}" >> /dev/stderr
    local FLOAT
    local SUFF
    local INT
    local ZATECKOU

    [ "${1//0/}" == "." ] && echo "0" || FLOAT=${1}
    SUFF=${FLOAT#*.}
    [ ${#SUFF} -gt 6 ] && SUFF=${SUFF:0:6}
    INT=${FLOAT%.*}
    ZATECKOU=${#SUFF}
    if [ "${INT}" == "0" ] ; then
        INT=""
        if [ "${SUFF:0:5}" -eq "00000" ] ; then
            SUFF=${SUFF:5}
        elif [ "${SUFF:0:4}" -eq "0000" ] ; then
            SUFF=${SUFF:4}
        elif [ "${SUFF:0:3}" -eq "000" ] ; then
            SUFF=${SUFF:3}
        elif [ "${SUFF:0:2}" -eq "00" ] ; then
            SUFF=${SUFF:2}
        elif [ "${SUFF:0:1}" -eq "0" ] ; then
            SUFF=${SUFF:1}
        fi
    fi
    case "${ZATECKOU}" in
        1) FLOAT="${INT}${SUFF}00000";;
        2) FLOAT="${INT}${SUFF}0000" ;;
        3) FLOAT="${INT}${SUFF}000" ;;
        4) FLOAT="${INT}${SUFF}00" ;;
        5) FLOAT="${INT}${SUFF}0" ;;
        6) FLOAT="${INT}${SUFF}" ;;
    esac
    [ ${DEBUG} ] && \
        echo "RETURN: $FUNCNAME() - FLOAT ${FLOAT}" >> /dev/stderr
    echo ${FLOAT}
    return
}

float_strip()

Ořezává nadbytečné nuly. Takže místo 0.000000 vrátí jen 0.0

float_strip () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - ${1}" >> /dev/stderr
    local MODULO
    local FLOAT

    MODULO="${1#*.}"
    case "${MODULO}" in
        [0-9][0-9][0-9][0-9][0-9][1-9]) FLOAT="${1}" ;;
        [0-9][0-9][0-9][0-9][1-9]0) FLOAT="${1%.*}.${MODULO:0:5}" ;;
        [0-9][0-9][0-9][1-9]00) FLOAT="${1%.*}.${MODULO:0:4}" ;;
        [0-9][0-9][1-9]000) FLOAT="${1%.*}.${MODULO:0:3}" ;;
        [0-9][1-9]0000) FLOAT="${1%.*}.${MODULO:0:2}" ;;
        [0-9]00000) FLOAT="${1%.*}.${MODULO:0:1}" ;;
    esac

    if [ ${FLOAT} ] ; then
        [ ${DEBUG} ] && \
            echo "RETURN: $FUNCNAME() - FLOAT ${FLOAT}" >> /dev/stderr
        echo ${FLOAT}
    else
        [ ${DEBUG} ] && \
            echo "RETURN: $FUNCNAME() - original value ${1}" >> /dev/stderr
        echo ${1}
    fi
    return
}

float_unify()

Přijme číslo typu float (čas), převede ho na řetězec a pomocí funkce float_strip() ořeže nadbytečné nuly.

Takže místo čísla 1.000000 vrátí jen 1.0, čas zapsaný jako 1.2300 upraví na 1.23

Funkce je nezbytná pokud se zapisuje čas do asciicastu. Tomu sice nadbytečné nuly nevadí, ale komplikuje to aktualizace značek

float_unify () {
    [ ${DEBUG} ] && \
        echo "INFO: float_unify() ${1}" >> /dev/stderr
    float_strip $(float_from_string $(float_to_string ${1}))
    return
}

line_by_inte()

Pokud číslo typu integer, může být řádkem události, vrátí se zpět. V opačném případě je návratový kód 1.

Je-li číslo záporné, předpokládá se, že jde o řádek události od konce záznamu. Tedy za předpokladu, že má záznam 15 událostí, vrátí

line_by_inte -5

hodnotu 11 = (15 + 1 hlavička) - 5.

line_by_inte -0

Vrátí 16 = (15 + 1 hlavička), ale

line_by_inte 0

nebo

line_by_inte -16

skončí chybou, protože nic jako řádek 0 neexistuje a celkový počet událostí s řádkem na kterém je hlavička je 16 (16 - 16 = 0)

line_by_inte () {
    [ ${DEBUG} ] &&
        echo "CALL: $FUNCNAME() - ${1}" >> /dev/stderr
    local TEST
    local ENDLINE

    ENDLINE=$((${#LINES[@]} + 1))
    case $(value ${1}) in
        nflo|flow|time) echo "ERROR: line_by_inte() not accept flow. You must use line_by_time()" >> /dev/stderr
            return 1
            ;;
        inte) [ "${1}" == "0" ] || [ "${1}" -gt "${ENDLINE}" ] && \
            echo "ERROR: line_by_inte() - Line ${1} is out of file, line ${ENDLINE} is the last event" && \
                return 1
            echo "${1}"
        ;;
        nint) TEST=$((${ENDLINE} + ${1}))
            [ "${TEST}" -lt 1 ] && echo "ERR: Parameter ${1} out of range" && return 1
            echo "${TEST}"
        ;;
        *) echo "ERROR: line_by_inte() -  Allowed type is only inte or nint. ${1} is type $(value ${1})." >> /dev/stderr
            return 1
        ;;
    esac
}

line_by_time()

Funkce by se měla použít všude tam, kde je potřeba na základě času, ať již v „human-ready” formě (23:45:01) nebo v sekundách (0.987654) zjistit číslo řádku události, která se spustí po tomto čase (je-li číslo kladné, typu float). Nebo události, která se spustí určitý čas před koncem záznamu – bude-li číslo typu float záporné. Akceptovány jsou tedy následující varianty:

line_by_time  1:23 # vrátí řádek události co se spustí v čase 83.000000
line_by_time  1.23 # vrátí řádek události co se spustí v čase  1.230000

nebo

line_by_time -0.000010

..která vrátí událost, co se spustí během posledních 10 mikrosekund před koncem záznamu

line_by_time () {
    [ ${DEBUG} ] &&
        echo "INFO: line_by_time(): \$1:\"$1\"" >> /dev/stderr
    local i
    local TEST
    local INTERVAL
    local ENDLINE
    local ENDTIME
    ENDLINE=$((${#LINES[@]} + 1))
    ENDTIME="${LINES[$((${ENDLINE}-2))]:1}"

    case $(value ${1}) in
        inte|nint) echo "ERROR: line_by_time() - For ínteger use line_by_inte()" >> /dev/stderr
            return 1
        ;;
        rang) echo "ERROR: line_by_time() - For rang use lines_by_rang()" >> /dev/stderr
            return 1
        ;;
        flow|time) if [ "${1//[0-9]/}" == ":" ] ; then
               1:23 => 83.000
                INTERVAL=$(float_to_string $(print_seconds ${1}))
            else
                INTERVAL=$(float_to_string ${1})
            fi
            [ ${DEBUG} ] && \
                echo "INFO: line_by_time() : Get event which started after ${INTERVAL} sec. from start" >> /dev/stderr
            i=${ENDLINE}
            [ ${DEBUG} ] && \
                echo "INFO: line_by_time() : TEST  ${ENDTIME} - ${INTERVAL}" >> /dev/stderr
           Pokud odpovídá interval času poslední události, zahrnuje celý soubor
            [ "${ENDTIME}" == "${INTERVAL}" ] && \
                echo "${ENDLINE}" && \
                return
            TEST=$((${ENDTIME} - ${INTERVAL}))
           Záporný výsledek indikuje že byl předán interval větší než je celkový čas
            [ ! -z "${TEST//[0-9]/}" ] && \
                echo "ERROR: line_by_time(): Value ${1} is out of range. Last event on line ${ENDLINE} start in $(float_to_string ${ENDTIME}) sec." && \
                return 1
            [ ${DEBUG} ] && \
                echo "INFO: line_by_time(): "${LINES[@]} |\
                    ${SED} 's/\x20/\n/g' |\
                    tac >> /dev/stderr
            i=${ENDLINE}
            echo "${LINES[@]}" |\
                ${SED} 's/\x20/\n/g' |\
                tac |\
                while read line ; do
                [ ${DEBUG} ] && \
                    echo "INFO: line_by_time(): ${1} - ${i} - ${INTERVAL} - ${line}" >> /dev/stderr
                [ "${line:1}" -le "${INTERVAL}" ] && \
                    echo "${i}" && \
                    return || \
                    ((i-=1))
                done
            return
        ;;
        nflo) INTERVAL=$(float_to_string ${1:1})
            [ ${DEBUG} ] && \
                echo "INFO: line_by_time() : Get event which started ${INTERVAL} sec. before last event time" >> /dev/stderr
            i=${ENDLINE}
            [ ${DEBUG} ] && \
                echo "INFO: line_by_time() : ${1} : ${ENDTIME} - ${INTERVAL}" >> /dev/stderr
           Pokud odpovídá interval času poslední události, zahrnuje celý soubor
            [ "${ENDTIME}" == "${INTERVAL}" ] && \
                echo "1" && \
                return
            TEST=$((${ENDTIME} - ${INTERVAL}))
           Záporný výsledek indikuje že byl předán interval větší než je celkový čas
            [ ! -z "${TEST//[0-9]/}" ] && \
                echo "ERROR: line_by_time(): Value ${1} is out of range. Last event on line ${ENDLINE} start in $(float_to_string ${ENDTIME}) sec." && \
                return 1
            [ ${DEBUG} ] && \
                echo "${LINES[@]}" |\
                    ${SED} 's/\x20/\n/g' |\
                    tac >> /dev/stderr
            echo "${LINES[@]}" |\
                ${SED} 's/\x20/\n/g' |\
                tac |\
                while read line ; do
                    [ "${line:1}" -lt "${TEST}" ] && \
                    [ "${line:1}" -eq "${TEST}" ] && \
                    echo "${i}" && \
                    return
                ((i-=1))
                [ ${DEBUG} ] && \
                    echo "${line:1} - ${i}" >> /dev/stderr
                [ "${TEST}" -gt "${line:1}" ] && \
                    echo "${i}" && \
                    return
            done
            echo "${i}"
            return
        ;;
        *) echo "ERROR: line_by_time() not accept type $(value ${1}): flow,nflo and time allowed" >> /dev/stderr
            return 1
        ;;
    esac
}

line_by_rang()

Tahle funkce, na rozdíl od funkcí line_by_inte() a line_by_time() může vracet více než jednu hodnotu. Podle toho, jak vypadá předaný rozsah, ale rozsah, u kterého bude druhá hodnota nižší než první se vždy vyhodnotí jako chybný!

Syntaxe rozsahu je:

[TIME:]START-END[:INTERVAL]

TIME, čas spuštění události, ani čas INTERVALu v něm přítomny být nemusí, ale znak mínus (-) mezi celými čísly typu integer, které odpovídají řádkům, musí být přítomen vždy!. START musí být menší, nebo stejné číslo jako END, větší než 0.

5-4 # neplatný rozsah
0-2 # neplatný rozsah

4-5 # OK, vrátí hodnoty 4 a 5
4-4 # OK, vrátí hodnotu 4

Rozsah může obsahovat také časy, oddělené dvojtečkami:

1.23:4-5:6.78

Jako první se uvádí TIME - čas spuštění události. V takovém případě funkce ověří, zda-li je uvedený čas pro daný rozsah platný. Rozsahy se totiž používají pouze pro série řádků se stejným časem. Vypomůže si funkcí #line_by_time, by měla vrátit číslo řádku, kterým rozsah začíná.

Naopak druhá hodnota udává čas, kdy má následovat událost, která už není součástí intervalu. Zjistí podle času 1.23 číslo řádku události a pokud bude

1.23:4-5

TODO

line_by_rang () {
    [ ${DEBUG} ] &&
        echo "INFO: line_by_rang(): \$1:\"$1\"" >> /dev/stderr
    local RANG

    if [ "${1//:/}" == "${1}" ] ; then
        echo "plain rang"
        echo "4-5" >> /dev/stderr
        echo TODO
        case "$(value ${1})" in
            time|flow) echo "INFO: line_by_rang() return lines if rang START on TIME ${i} sec."
                echo TODO
            ;;
            nflo|nint) echo "ERROR: line_by_rang() - Invalid rang ${1}" >> /dev/stderr
                return 1
            ;;
            inte) echo "INFO: line_by_rang() - Return rang lines if ${1} line is from RANG" >> /dev/stderr
            ;;
            rang)
                echo "INFO: line_by_rang() - Return rang lines 4-5 without check time" >> /dev/stderr
                echo TODO
            ;;
            *) echo "ERROR: line_by_rang() not accept type $(value ${1}). Allowed only rang" >> /dev/stderr
            return 1
        ;;
        esac
    else
        RANG=(${1//:/ })
        case "${#RANG[@]}" in
            1) echo "ERROR: line_by_rang() - For test value $(value ${1}) use line_by_time()" >> /dev/stderr
            return 1
            ;;
            2) echo "INFO: line_by_rang() - Test time ${RANG[@]}" >> /dev/stderr
                echo "1.23:4 nebo 1.23:4-5 nebo 4-5:6.75 nebo 5:6.75" >> /dev/stderr
                echo TODO
            ;;
            3) echo "INFO: line_by_rang() - Full test ${RANG[@]}" >> /dev/stderr
                echo "1.23:4-5:6.75" >> /dev/stderr
                echo TODO
            ;;
            *) echo "ERROR: line_by_rang() ${RANG[@]} not accept type $(value ${1}). Allowed only rang" >> /dev/stderr
                return 1
            ;;
        esac
    fi
}

lineblock()

Zalamuje dlouhé řádky do bloku, podle aktuální velikosti terminálu

lineblock()
{
    [ ${DEBUG} ] && \
        echo "INFO: lineblock() ${1}" >> /dev/null

    if [ -z "${VIEW}" ] ; then
        sed -n ${1}p ${RECORDFILE}
    else
        sed -n ${1}p ${RECORDFILE} | fold -s -w $((${COLUMNS} - 20)) | sed '/^[^[{]/s/^/\t\|\t/'
    fi
}

line_o()

Vypíše na standardní výstup obsah řádku, na kterém je zobrazovaná událost, typu o (out)

line_o () {
    [ ! -z "${VIEW}" ] && \
        printf "%4d:%4d: " ${1} ${2}
    lineblock ${1}
    [ ! -z "${VIEW}" ] && \
        printf ""
    return
}

line_h()

Vypíše na standardní výstup obsah hlavičku asciicastu

line_h () {
    if [ -z "${VIEW}" ] ; then
        H={"version": 2
        H=${H},"width":${ORIGWIDTH}
        H=${H},"height":${ORIGHEIGHT}
        H=${H},"timestamp":${ORIGTIME}
        H=${H},"title": "
        if [ -z ${TITLE} ] ; then
            H=${H}"${ORIGTITLE}"
        else
            H=${H}"${TITLE}"
        fi
        H=${H}","duration":$(float_from_string ${DURATION})
        [ ! -z ${IDLE} ] && \
            H=${H},"idle_time_limit":${IDLE}
      if [ ! -z ${FG} ] || \
          [ ! -z ${BG} ] || \
          [ ! -z ${PALLETE} ] ; then
              echo theme >> /dev/null
              #ORIGFG
              #ORIGBG
              #ORIGPALLETE
      fi
        H=${H},"env": {"SHELL":${ORIGSHELL}
        H=${H},"TERM":${ORIGTERM}
        H=${H}}}

        echo ${H}
    else
        H={\n\t\t"version":\t2
        H=${H},\n\t\t"width":\t${ORIGWIDTH}
        H=${H},\n\t\t"height":\t${ORIGHEIGHT}
        H=${H},\n\t\t"timestamp":\t$(date +%s)
        H=${H},\n\t\t"title":\t"
        if [ -z ${TITLE} ] ; then
            H=${H}"${RECORDFILE##*/}"
        else
            H=${H}"${TITLE}"
        fi
        H=${H}",\n\t\t"duration":\t$(float_from_string ${DURATION})
        [ ! -z ${IDLE} ] && \
            H=${H},\n\t\t"idle_time_limit":\t${IDLE}
      if [ ! -z ${FG} ] || \
          [ ! -z ${BG} ] || \
          [ ! -z ${PALLETE} ] ; then
              echo theme >> /dev/null
              #ORIGFG
              #ORIGBG
              #ORIGPALLETE
      fi
        H=${H},\n\t\t"env": {\n\t\t\t"SHELL": ${ORIGSHELL}
        H=${H},\n\t\t\t"TERM": ${ORIGTERM}
        H=${H}\n\t\t}\n\t}

        printf "%4d:%4d: " 1 1
        echo -e ${H}
        printf ""
    fi
    return
}

line_m()

Vypíše na standardní výstup obsah řádku, na kterém je událost typu m (značka - marker)

line_m () {
    [ ! -z "${VIEW}" ] && \
        printf "%4d:%4d: " ${1} ${2}
    lineblock ${1}
    [ ! -z "${VIEW}" ] && \
        echo -n "" >> /dev/stderr
    return
}

line_r()

Vypíše na standardní výstup obsah řádku, na kterém je událost typu r (resize)

line_r () {
    [ ! -z "${VIEW}" ] && printf "%4d:%4d: " ${1} ${2}
        lineblock ${1}
    [ ! -z "${VIEW}" ] && echo -n ""
    return
}

get_scope()

Vypíše série událostí, se stejným časem, ve formátu rang

0.051663:0-60
 ^       |  \
 |        \  řádek poslední události
  \        řádek první události
   čas událostí

get_scope () {
    [ ${DEBUG} ] && echo "INFO: get_scope()" >> /dev/stderr

    for i in ${!LINES[@]} ; do
        TIME="${LINES[${i}]:1}"
        NEXT="${LINES[$((${i}+1))]:1}"
        [ -z ${NEXT} ] && return
        if [ "${TIME}" -eq "${NEXT}" ] ; then
            [ -z "${FRAME}" ] && FRAME="$(float_from_string ${TIME}):$((${i}+2))"
        else
            [ ${FRAME} ] && echo "${FRAME}-$((${i}+2))" && FRAME=''
        fi
    done
    return
}

get_line()

Vypíše číslo řádku na kterém je uveden přesný čas

get_line () {
  [ ! -z "${VIEW}" ] && printf "%4d:%4d: " ${1} ${2}
      lineblock ${1}
  [ ! -z "${VIEW}" ] && echo -n ""
    grep -n -m 1 "^\[${1},\ " ${RECORDFILE}
    return
}

playcast()

Tato funkce, volaná při akci play není plnohodnotným přehrávačem asciicastu, protože viditelná změna je pozorovatelná až u časového intervalu na úrovni čtvrtého řádu (stovky mikrosekund).

Jak dlouhé jsou intervaly u vašeho záznamu, můžete velice snadno zjistit, použijete na vybranou sekvenci událostí ve smyčce příkaz context:

~$ for i in {34..69} ;\
   do asciiframe view comment.cast context $i \
   done 2>/dev/null | sort -n
0.241604 (0.110383 - 0.131221)
0.272310 (0.094456 - 0.177854)
...

Po setřídění dle hodnoty intervalu, který události předcházel (první číslo v závorce):

...
done 2>/dev/null | awk -F\( '{print $2}' | awk '{print $1}' | sort -n
0.091274
...
1.695180

Zjistíte, že se pohybuje od 0.1 do 0.3 sekundy. Kratší, nebo naopak delší interval už signalizuje menší zaváhání pisatele.

Načítá postupně jednotlivé položky z pole [[#$LINES|${LINES[@]}]] a výstup posílá na PTS terminály adresované parametrem $1 a $2'

playcast () {
    echo '''
[layouts]
  default
    [window0]
      type = Window
      parent = ""
    [child1]
      type = Terminal
      parent = window0
      profile = minisize
''' > ${TEMP_DIR}/2-rows.json
    #${TERMINATOR} -g ${TEMP_DIR}/2-rows.json &>/dev/null
    TIME=0
    for i in ${!LINES[@]} ; do
        if [ $i -eq 0 ] ; then
            echo >> /dev/null
        else
            LINE=$(sed -n $((i+1))p ${RECORDFILE})
            TEXT="${LINE##*\", \"}"
            FRAME="${TEXT%\"*}"
            #echo "${FRAME}" >> /dev/stderr
            #STRING=$((${#TEXT} - 2))
            #CONTENT="${LINE:${#FRAME}:${STRING}}"
            case ${LINES[${i}]:0:1} in
                m)MARKER
                    printf "%s %12s – %12s\r" m $(float_from_string ${LINES[${i}]:1}) $(float_from_string ${LINES[$((${i} + 1))]:1}) > /dev/pts/${1}
                ;;
                o)OUT
                    printf "%s %12s – %12s\r" o $(float_from_string ${LINES[${i}]:1}) $(float_from_string ${LINES[$((${i} + 1))]:1}) > /dev/pts/${1}
                    printf "${FRAME}" > /dev/pts/${2}
                ;;
                r)RESIZE
                    printf "%s %12s – %12s\r" o $(float_from_string ${LINES[${i}]:1}) $(float_from_string ${LINES[$((${i} + 1))]:1})> /dev/pts/${1}
                ;;
            esac
            sleep $(float_from_string $((${LINES[${i}]:1} - ${TIME})))
            TIME=${LINES[${i}]:1}
        fi
    done
    return
}

Tahle funkce, kterou volá funkce get_view(), vypisuje na standardní výstup události, obarvené podle typu a doplněné kromě čísla řádku také pořadovým číslem.

$1 je typ události (m, o nebo

r a nepoviný parametr $2 může být pořadové číslo události. Viz příklad:

print_event r 2

V tomto případě funkce vrátí řádek události r (resize) která je v rámci souboru druhá v pořadí.

O samotný tisk událostí se starají funkce:

line_m()
line_o()
line_r()

print_event () {
    local PRINT
    case ${1} in
        m|o|r) PRINT="line_${1}" ;;
        *) return
    esac
    z=0
    for i in ${!LINES[@]} ; do
        case ${LINES[$i]:0:1} in
            ${1}) ((z=z+1))
                if [ -z ${2} ] ; then
                    VIEW="yes"
                    ${PRINT} $(($i+2)) $z
                else
                    if [ $z -eq ${2} ] ; then
                        VIEW="yes"
                        ${PRINT} $(($i+2)) $z
                        break
                    fi
                fi
            ;;
        esac
    done
    return
}

TODO

print_monochrome () {
    tac ${RECORDFILE} | sed 's/\\/\\\\/g' | while read line ; do
        LINE=$(event_plain "${line}")
        ITEM=${LINE::-2}
        if [ "${ITEM}" == "${ITEM%\\u001b[K}" ] ; then
            if [ ${BACK-0} -eq 0 ] ; then
                echo "${LINE}"
            else
                TEST="${ITEM##*\"}"
                case ${#TEST} in
                    1) BACK=$(($BACK - 1))
                    ;;
                    *) echo BACKOVER >> /dev/stderr
                        echo ${TEST} >> /dev/stderr
                    ;;
                esac
            fi
        else
            TEST="${ITEM%\\u001b[K}"
            if [ "${TEST##*\"}" == '\b' ] ; then
                BACK=$((${BACK} + 1))
            else
                echo "${LINE}"
            fi
        fi
    done | tac | sed 's/\\/\\\\/g' | while read line ; do
       Opakovaný cyklus je nutný z toho důvodu, že některé
       speciální sekvence mohou být rozdělené přes dvě události,
       takže nemusí být během prvního cyklu odchyceny
        [ ! -z "${DEBUG}" ] && echo "${line}"
        ITEM="${line::-2}"
        if [ "${ITEM}" != "${ITEM%u001b}" ] ; then
            SLOUCIT="${ITEM}"
            [ ! -z "${DEBUG}" ] && echo "SPECIAL CHAR on END LINE" >> /dev/stderr
        elif [ "${ITEM}" != "${ITEM%[0-9];}" ] ; then
            SLOUCIT="${ITEM}"
            [ ! -z "${DEBUG}" ] && echo "PROBABLY SPECIAL CHAR on END LINE" >> /dev/stderr
        else
            if [ -n "${SLOUCIT}" ] ; then
                [ ! -z "${DEBUG}" ] && echo "CONCATE" >> /dev/stderr
                event_plain "${SLOUCIT}${line##*,\ \"}"
                [ ! -z "${DEBUG}" ] && echo "CONCATED" >> /dev/stderr
                SLOUCIT=''
            else
                echo "${line}"
            fi
        fi
    done
}

value()

Funkce přebere hodnotu parametru $1, kterou zpracuje jako textový řetězec a na záklatě toho vrátí některou níže uvedených řetězcových hodnot.

Každá má délku 4 znaků a vychází z toho, jakému typu $1 odpovídá. Čísla, protože mohou mít i negativní variantu, mohou vracet také varianty začínající znakem n, za který následují první tři znaky odpovídající typu čísla: flo nebo int.

flow je kladné číslo s plovoucí čárkou, které zastupuje časový interval v sekundách. Ve skriptu se veškerá čísla převádí na řetězcovou hodnotu kladného celého čísla, takže místo čísla 0.000001 (float) se pracuje s číslem 1 (integer)

file se vrací v případě, že je parametrem existující soubor.

help - `obsolete`

inte je kladné, celé číslo, jako je například číslo 12. Obvykle jde o číslo řádku, pořadí události. Podle typu akce.

nflo je záporné číslo s plovoucí čárkou typu float např. -12.345678

nint je záporné celé číslo typu integer, např. -12. S takovými čísly pracuje hlavně akce view, kdy znaménko mínus signalizuje že jde o řádky co předcházejí

rang je řetězec, který může kromě čísel a tečky obsahovat také pomlčku. Lze takto v jednom řetězci vymezit rozsah (range), ale v současné chvíli se s takovým typem řetězců nepracuje.

stri zastupuje řetězcovou hodnotu. Obvykle název příkazu, akce, nebo zástupného řetězce pro speciální sekvence.

time zastupuje čas předaný ve tvaru HH:MM:SS, který je přirozenější pro naše vnímání času. Interně se tenhle typ řetězce převádí na flow (čas v sekundách)

value () {
    if [ -z "${1}" ] ; then
        return
    elif [ ${1:0:1} == "-" ] ; then
        NUM=${1:1}
        if [ -z "${NUM//:digit:/}" ] ; then
            echo nint
        elif [ "${NUM//:digit:/}" == "." ] ; then
            echo nflo
        else
            echo help
        fi
    elif [ -z "${1//:digit:/}" ] ; then
        echo inte
    elif [ "${1//:digit:/}" == ":" ] ; then
        echo time
    elif [ "${1//:alnum:/}" == "${1//:digit:/}" ] ; then
        if [ "${1//-/}" == "${1}" ] ; then
            echo flow
        else
            echo rang
        fi
    else
        if [ -f "${1}" ] ; then
            echo file
        else
            echo stri
        fi
    fi
    return
}

clip_load()

Funkce si nejprve ověří, zda-li existuje soubor ${CLIPFILE}. Pokud ano, nakrmí pomocí aplikace ${JQ} pole ${CLIPLINES[@]}, položkami, které jsou stejně jako u pole ${LINES[@]} kombinací typu události a času, konvertovaného z typu float na řetězec.

clip_load () {
    [ ${DEBUG} ] && \
        echo "INFO: clip_load() ${CLIPFILE} " >> /dev/stderr
    [ ! -f ${CLIPFILE} ] && return 1
    local HEADER
    local TEST

    TEST=$(head -1 ${CLIPFILE})
    HEADER="${TEMP_DIR}/clipheader"
    ${JQ} '.width, .height, .timestamp, .title' <<< ${TEST} > ${HEADER}
   width
    head -1 ${HEADER} | tail -1 | sed 's/\"//g' > ${TEMP_DIR}/cwidth
   height
    head -2 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/cheight
   timestamp
    head -3 ${HEADER} | tail -1 >  ${TEMP_DIR}/ctimestamp
   title (nepovinný string)
    head -4 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/ctitle

    [ ${DEBUG} ] && \
        echo "INFO: clip_load() load ${CLIPFILE} events" >> /dev/stderr
    TEST=($(jq 'if [.[] ] | .[1] == ("m", "o" , "r") then "\(.[1])\(.[0] * 1000000 | floor | tostring)" else null end' ${CLIPFILE}))
    CLIPLINES=(${TEST[@]//[nul\"]/})
    return
}

header_load()

Natahuje hlavičku asciicastu, před načtením událostí

echo $TEST | jq '.version, ... , .env.THERM'

nebo

jq '.version, ... , .env.THERM' <(echo ${TEST})

header_load () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - ${1}" >> /dev/stderr
    local HEADER
    local TEST

    TEST=$(head -1 ${1})
    HEADER="${TEMP_DIR}/header"
    if [ ${#TEST} -eq 1 ] ; then
        RECORDFILE="${TEMP_DIR}/json_to_ascii.cast"
        [ ${DEBUG} ] && \
            echo "WARN: $FUNCNAME() convert JSON ${1} into asciicast ${RECORDFILE}" >> /dev/stderr
        ${JQ} . -c -M ${1} > ${RECORDFILE}
        ${JQ} '.version, .width, .height, .timestamp, .title, .duration, .idle_time_limit, .env.SHELL, .env.TERM, .theme.fg, .theme.bg, .theme.pallete' <(head -1 ${RECORDFILE}) > ${HEADER}
    else
        RECORDFILE=${1}
        ${JQ} '.version, .width, .height, .timestamp, .title, .duration, .idle_time_limit, .env.SHELL, .env.TERM, .theme.fg, .theme.bg, .theme.pallete' <<< ${TEST} > ${HEADER}
    fi
   version
    head -1 ${HEADER} | tail -1 >  ${TEMP_DIR}/version
   width
    head -2 ${HEADER} | tail -1 | sed 's/\"//g' > ${TEMP_DIR}/width
   height
    head -3 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/height
   timestamp
    head -4 ${HEADER} | tail -1 >  ${TEMP_DIR}/timestamp
   title (nepovinný string)
    head -5 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/title
   duration (nepovinný float)
    head -6 ${HEADER} | tail -1 >  ${TEMP_DIR}/duration
   idle (nepovinný float)
    head -7 ${HEADER} | tail -1 >  ${TEMP_DIR}/idle
   shell string
    head -8 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/shell
   term string
    head -9 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/term
   barva textu string
    head -10 ${HEADER} | tail -1 | sed 's/\"//g' > ${TEMP_DIR}/fg
   barva pozadí string
    head -11 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/bg
   paleta barev string
    head -12 ${HEADER} | tail -1 | sed 's/\"//g' >  ${TEMP_DIR}/pallete
    [ ${DEBUG} ] && \
        cat ${HEADER} >> /dev/stderr && \
        ls -al ${TEMP_DIR} >> /dev/stderr && \
        echo "RETURN: $FUNCNAME() DONE!" >> /dev/stderr
    return
}

cast_load()

Tato funkce načítá události ze souboru ${RECORDFILE} pomocí aplikace ${JQ} do pole ${LINES[@]}.

${LINES[@]}, obsahuje řetězce, které jsou kombinací typu události a času, převedeného z typu float na celé číslo typu integer což je čas v mikrosekundách. Počet položek v tomto poli poodpovídá počtu událostí (řádků) v asciicastu.

Funkce umožňuje také konverzi asciicastu ve formátu v1 na novější formát v2

cast_load () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - RECORDFILE ${RECORDFILE}" >> /dev/stderr
    local JSON
    local TEST

    case $(head -1 ${TEMP_DIR}/version) in
        1) echo "WARN: Original is v1 format (JSON), wait to conversion into v2 format. Export output into a new clip is recommend." >> /dev/stderr
            echo "2" > ${TEMP_DIR}/version
            TITLE="$(head -1 ${TEMP_DIR}/title) - converted from v1 asciicast format"
           new timestamp
            echo $(date +%s) > ${TEMP_DIR}/timestamp
            ${JQ} . -M ${RECORDFILE} | ${SED} 's/\\/\\\\/g' | while read line ; do
                #echo "${line}" >> /dev/stderr
                if [ "${line}" == '"stdout": [' ] ; then
                    JSON=yes
                    TEST=0
                    header_print
                elif [ ${JSON} ] ; then
                    case ${line} in
                        \[)
                        ;;
                        [0-9]*) TEST=$(( ${TEST} + $(float_to_string ${line// ,/}) ))
                        ;;
                        \"*) echo "[$(float_from_string ${TEST}), \"o\", ${line}]"
                        ;;
                    esac
                fi
            done > ${TEMP_DIR}/ascii.cast
            RECORDFILE=${TEMP_DIR}/ascii.cast
            TEMPORARY=($(jq 'if [.[] ] | .[1] == ("m", "o" , "r") then "\(.[1])\(.[0] * 1000000 | floor | tostring)" else null end'  ${RECORDFILE}))
            LINES=(${TEMPORARY[@]//[nul\"]/})
            [ ${DEBUG} ] && \
                echo "RETURN: $FUNCNAME() array LINES (${#LINES[@]}) ${LINES[@]}" >> /dev/stderr
        ;;
        2) TEMPORARY=($(jq 'if [.[] ] | .[1] == ("m", "o" , "r") then "\(.[1])\(.[0] * 1000000 | floor | tostring)" else null end'  ${RECORDFILE}))
            LINES=(${TEMPORARY[@]//[nul\"]/})
            [ ${DEBUG} ] && \
                echo "RETURN: $FUNCNAME() array LINES (${#LINES[@]}) ${LINES[@]}" >> /dev/stderr
        ;;
        *) echo "WARN: This version of the asciicast format CLI player don't support" >> /dev/stderr
        ;;
    esac
    return
}

get_string()

Vrací řetězce, které se mohou vyskytovat v asciicastu. Jejich seznam je uložen v poli?

https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html

get_string () {
    [ ${DEBUG} ] && \
        echo "INFO: get_string() ${1} - ${2}" >> /dev/stderr
    local STRING

    [ ${2} ] && STRING=${2}
    case ${1} in
        backline|\
        K)Používá se, při nahrazení zobrazeného řetězce jiným
           Kurzor se vrátí a následující události původní řetězec
           přepíšou
            echo "[${STRING}K"
        ;;
        backchar)Používá se, při nahrazení zobrazeného řetězce jiným
           Kurzor se vrátí a následující události původní řetězec
           přepíšou
            echo "\b"
        ;;
        J)clear screen - STRING 1 (before) - 2 (after)
            echo "[${STRING}J"
        ;;
        clear)variant: blank 2
            echo '\u001b[2J'
        ;;
        breakline) echo "\u0007"
        ;;
        color)color (0 - off) STRING i.e. 1;37
            echo "[${STRING}m"
        ;;
        select)variant of: color 7
            echo '\\\u001b\[7m'
        ;;
        blink) echo 'Sekvence skryje výplň kurzoru' >> /dev/stderr
            echo '='
        ;;
        enter)start new line for command
            echo "]0"
        ;;
        A|\
        cursor-up)move cursor up
            echo "[${STRING}A"
        ;;
        B|\
        cursor-down)move cursor down
            echo "[${STRING}B"
        ;;
        C|\
        cursor-fore)move cursor forward
            echo "[${STRING}C"
        ;;
        D|\
        cursor-back)move cursor back
            echo "[${STRING}D"
        ;;
        H|\
        cursor)skip to start line STRING (target row to skip)
            echo "[${STRING}H"
        ;;
        cursor-restore)save actual cursor position
            echo "[u"
        ;;
        cursor-hide)enter 1 2004 (number may be replaced by dot)
            echo "[?${STRING}l"
        ;;
        cursor-save)save actual cursor position
            echo "[s"
        ;;
        cursor-show)enter 1 2004 (number may be replaced by dot)
            echo "[?${STRING}h"
        ;;
        newline)'\r\n'
            echo "\r\n"
        ;;
        return) echo "\r"
        ;;
        *) [ ${DEBUG} ] && \
            echo "find – ${1}" >> /dev/stderr
            printf "${1}"
        ;;
    esac
    return
}

time_by_line()

Vstupním parametrem $1 je číslo řádku. Funkce vrací přesný čas události na tomto řádku v mikrosekundách (kladné číslo typu integer'). Při záporné hodnotě, vrací čas události předchozí

time_by_line () {
    [ ${DEBUG} ] && \
        echo "INFO: time_by_line() : ${1}" >> /dev/stderr
    local INDEX

    case $(value ${1}) in
        inte) INDEX=$((${1} - 1))
        ;;
        nint) INDEX=$((${1:1} - 2))
        ;;
    esac
    echo ${LINES[${INDEX}]:1}
    return
}

Parametrem $1 je čas, který byl předán ve tvaru 00:00:01 Teoreticky je možné předat čas delší než 02:45:60 ale tak dlouhý asciicastový záznam by stejně nikdo nesledoval – většina ze 75 tisíc veřejných záznamů na serveru https://asciinema.org netrvá déle než minutu.

print_seconds () {
    [ ${DEBUG} ] && \
        echo "INFO: print_seconds() : ${1}" >> /dev/stderr

    case ${#1} in
        1) printf "%d.000000\n" $(date "+%s" --date="1970-01-01 00:00:0${1} UTC");;
        2) printf "%d.000000\n" $(date "+%s" --date="1970-01-01 00:00:${1} UTC")
        ;;
        4) printf "%d.000000\n" $(date "+%s" --date="1970-01-01 00:0${1} UTC") ;;
        5) printf "%d.000000\n" $(date "+%s" --date="1970-01-01 00:${1} UTC")
        ;;
        7) printf "%d.000000\n" $(date "+%s" --date="1970-01-01 0${1} UTC") ;;
        8) printf "%d.000000\n" $(date "+%s" --date="1970-01-01 ${1} UTC")
        ;;
    esac
    return
}

Parametrem $1 je čas v mikrosekundách, předaný buď kladné celé číslo typu integer, nebo jako číslo typu float, s tečkou místo čárky – např. 0.0 a výsledkem čas zaokrouhlený na celé sekundy, ve tvaru "00:00:00" (hodiny, minuty a sekundy). K převodu se využívá date.

print_time () {
    [ ${DEBUG} ] && \
        echo "INFO: print_time() : ${1}" >> /dev/stderr

    case $(value ${1}) in
        inte) date -ud "@$(float_from_string ${1})" +%T
        ;;
        flow) date -ud "@${1}" +%T
        ;;
    esac
    return
}

get_view()

Parametrem $1 je pořadové číslo výchozího řádku, který se zobrazí jako první, které nemůže být vyšší než je aktuální počet řádků v asciicastu, které musí být předáno vždy

Parametr $2, kladné celé číslo, určuje, kolik se má zobrazit řádků po něm následujících. Je nepovinný. Není-li uveden, zobrazí se pouze výchozí řádek

info

Zobrazí informace o asciicastu

scope

Pokud asciicast obsahuje série událostí, co se zobrazují ve stejný čas, zobrazí jejich RANG

context

O jak velký časový interval je potřeba události posunout, se dá zjistit přes kontext události:

~$ asciiframe.sh view ascii.cast context 21
2.997355 (2.334431 - 0.662924)

První číslo je délka intervalu mezi zobrazením předchozí a zobrazením následující události – to je pracovní prostor pro akci event. První číslo v závorce, je časový interval, od spuštění předchozí události, která je v asciicastu na řádku č. 20 a druhé číslo je čas, který uplyne, než dojde na událost, co je na řádku č. 22

O jaké události jde, zjistíte velice snadno. Stačí si zobrazit přes akci view řádek č. 20 + 2 řádky následující:

~$ asciiframe.sh view ascii.cast 20 2
...

nebo naopak řádek č. 22 + 2 řádky předchozí:

~$ asciiframe.sh view ascii.cast 20 -2
...

V obou případech bude výsledek stejný. Příkaz context, předaný akci ''view nám umožní zjistit optimální prodlení pokud budeme chtít obsah zobrazované události z řádku č. 21 rozdělit.

get_view () {
    [ ${DEBUG} ] && \
        echo "CALL: $FUNCNAME() - ${1} - ${2}" >> /dev/stderr
    local CONTEXT
    local EVENT
    local NEXT
    local ITEM
    local PREV
    local TEST
    local TIME

    case ${1} in
        -0|0) echo "ERROR: Line ordering started from 1, not ${1}" && return 1
        ;;
        context)context
            case $(value ${2}) in
                flow|time) if [ "${2/:/}" == "${2}" ] ; then
                        TIME=$(float_unify ${2})
                        LINE=$(line_by_time ${TIME})
                    else
                        LINE=$(line_by_time $(print_seconds ${2}))
                    fi
                    [ $? -eq 1 ] && \
                        echo ${LINE} && \
                        return 1
                    context_show ${LINE}
                ;;
                inte|rang) FROM=(${2/-/ })
                    if [ -z "${FROM[0]#*:}" ] ; then
                        [ -z "${FROM[1]%:*}" ] && return 1 || FROM[0]=${FROM[1]%:*}
                    elif [ -z "${FROM[1]%:*}" ] ; then
                        FROM[1]=${FROM[0]#*:}
                    fi
                    for i in $(seq ${FROM[0]#*:} ${FROM[1]%:*}) ; do
                        printf "%d: %s\n" "$i" "$(context_show ${i})"
                    done
                ;;
            esac
            return
        ;;
        info)show title, time of record (timestamp), time last change
            TEST=$(head -1 ${TEMP_DIR}/title)
            [ "${TEST}" == 'null' ] && TEST='not set'
            printf "%18s : %s\n" 'title' "${TEST}"
            TEST=$(head -1 ${TEMP_DIR}/timestamp)
            printf "%18s : %s (%d)\n" 'record date' $(date --date="@${TEST}") "${TEST}"
            TEST="${#LINES[@]}"
            printf "%18s : %s\n" 'events count' "${TEST}"
            TEST=$(stat -c %s ${RECORDFILE})
            printf "%18s : %s\n" 'size in bytes' "${TEST}"
            TEST=$(head -1 ${TEMP_DIR}/duration)
            [ "${TEST}" == 'null' ] && TEST='not set'
            printf "%18s : %s\n" 'duration' "${TEST}"
            TEST=$(float_from_string ${LINES[-1]:1})
            printf "%18s : %s sec. (%s)\n" 'time of last event' "${TEST}" $(do_event time ${TEST})
            TEST=$(stat -c %w ${RECORDFILE})
            printf "%18s : %s\n" 'created' "${TEST}"
            TEST=$(stat -c %y ${RECORDFILE})
            printf "%18s : %s\n" 'last change' "${TEST}"
            unset TEST
            return
        ;;
        scope)scope
            case $(value ${2}) in
                flow|time) if [ "${2/:/}" == "${2}" ] ; then
                        TIME=$(float_unify ${2})
                    else
                        LINE=$(line_by_time $(print_seconds ${2}))
                        [ $? -eq 1 ] && \
                            echo ${LINE} && \
                            return 1
                        echo ${LINE} >> /dev/stderr
                        TIME=$(float_from_string $(time_by_line ${LINE}))
                    fi
                    get_scope | while read line ; do
                        if [ "$(float_strip ${line%%:*})" == "${TIME}" ] ; then
                            echo ${line}
                            break
                        fi
                    done
                ;;
                nflo) echo "ERROR: get_view() ${1} not accept $(value ${2}) ${2}"
                    return 1
                ;;
                inte|rang) FROM=(${2/-/ })
                    if [ -z "${FROM[0]#*:}" ] ; then
                        [ -z "${FROM[1]%:*}" ] && return 1 || FROM[0]=${FROM[1]%:*}
                    elif [ -z "${FROM[1]%:*}" ] ; then
                        FROM[1]=${FROM[0]#*:}
                    fi
                    get_scope | while read line ; do
                        RANG=(${line/-/ })
                        START=${RANG[0]#*:}
                        END=${RANG[1]%:*}
                        for i in $(seq ${START} ${END}) ; do
                            for y in $(seq ${FROM[0]#*:} ${FROM[1]%:*}) ; do
                                [ $i -eq $y ] && echo ${line} && return
                            done
                        done
                    done
                ;;
                *) get_scope
                ;;
            esac
            return
        ;;
        h)header
            jq --slurp '.[0]' ${RECORDFILE}
            return
        ;;
        m)marker
            print_event m ${2}
            return $?
        ;;
        o)out
            print_event o ${2}
            return $?
        ;;
        r)resizes
            print_event r ${2}
            return $?
        ;;
        *) case $(value ${1}) in
                nflo) [ ${DEBUG} ] && \
                    echo "INFO: get_view() negative time START" >> /dev/stderr
                    TEST=$((${LINES[$((${#LINES[@]}-1))]:1} - $(float_to_string ${1:1})))
                    [ ${TEST:0:1} -le 1 ] && echo "ERR: get_view() Parameter ${1} out of range" && return 1
                    #echo "Spočítat řádek" >> /dev/stderr
                    x=''
                    for i in ${!LINES[@]} ; do
                        if [ ${LINES[$i]:1} -ge ${TEST} ] ; then
                            x=$i;
                            break
                        fi
                    done
                    if [ -z ${x} ] ; then
                        echo "INFO: get_view() not match for value ${1}" >> /dev/stderr
                    else
                        BEGIN=$((${x}+1)) >> /dev/stderr
                        shift
                    fi
                ;;
                flow) [ ${DEBUG} ] && \
                        echo "ERR: get_view() flow positive $(line_by_time ${1}) time START" >> /dev/stderr
                    TEST=$((${LINES[$((${#LINES[@]}-1))]:1} - $(float_to_string ${1})))
                    [ ${TEST:0:1} -le 1 ] && echo "ERR: get_view() flow - Parameter ${1} out of range" && return 1
                    #echo "Spočítat řádek" >> /dev/stderr
                    TEST=$(float_to_string ${1})
                    x=''
                    for i in ${!LINES[@]} ; do
                        if [ ${LINES[$i]:1} -ge ${TEST} ] ; then
                            x=$i;
                            break
                        fi
                    done
                    if [ -z ${x} ] ; then
                        [ ${DEBUG} ] && \
                        echo "ERR: get_view() flow - Not match for value ${1}" >> /dev/stderr
                    else
                        BEGIN=$((${x}+1)) >> /dev/stderr
                        shift
                    fi
                ;;
                inte) #echo "# positive START" >> /dev/stderr
                    [ ${1} -gt $((${#LINES[@]}+1)) ] && \
                        echo "ERR: get_view() inte - Parameter ${1} out of range" && return 1
                    BEGIN=${1}
                    shift
                ;;
                nint) [ ${DEBUG} ] && \
                        echo "INFO: get_view()negative START" >> /dev/stderr
                    BEGIN=$((${#LINES[@]} + 1 ${1}))
                    [ ${BEGIN} -lt 1 ] && echo "ERR: get_view() nint - Parameter ${1} out of range" && return 1
                    shift
                ;;
                *) echo "ERROR: get_view() invalid value $(value ${1}) - ${1}" >> /dev/stderr
                    return 1
                ;;
            esac
    esac

    if [ -z ${DURATION} ] ; then
        DURATION=${LINES[$((${#LINES[@]}-1))]:1}
    fi
  echo "DURATION ${DURATION}" >> /dev/stderr
  echo "BEGIN ${BEGIN}" >> /dev/stderr
    if [ -z ${1} ]  ; then
        #echo "Není jiný parametr" >> /dev/stderr
        FROM=${BEGIN}
        TO=${BEGIN}
    else
        #echo "Je další parametr" >> /dev/stderr
        case $(value ${1}) in
            nflo) TEST=$((${LINES[$((${BEGIN}-2))]:1} - $(float_to_string ${1:1})))
                if [ "${TEST}" -le 1 ] ; then
                    FROM=2
                else
                    x=''
                    for i in ${!LINES[@]} ; do
                        if [ ${LINES[$i]:1} -le ${TEST} ] ; then
                            ((x=x+1))
                            if [ ${LINES[$i]:1} -ge ${LINES[$((${BEGIN}-2))]:1} ] ; then
                                break
                            fi
                        fi
                    done
                    FROM=$x
                fi
                TO=${BEGIN}
               nutné
                unset TEST
           ;;
            flow) #echo "Od ${BEGIN} flow ${1}" >> /dev/stderr
                FROM=${BEGIN}
                if [ ${FROM} -le 1 ] ; then
                    TEST=$(float_to_string ${1})
                else
                    TEST=$((${LINES[$((${BEGIN}-1))]:1} + $(float_to_string ${1})))
                fi
                for i in ${!LINES[@]} ; do
                    if [ ${LINES[$i]:1} -ge ${TEST} ] ; then
                        x=$i;
                        break
                    fi
                done
                if [ -z ${x} ] ; then
                    TO=${#LINES[@]}
                else
                    TO=$((${x}+1))
                fi
               nutné
                unset TEST
            ;;
            inte) #echo "BEGIN ${BEGIN} inte ${1}" >> /dev/stderr
                FROM=${BEGIN}
                TO=$((${BEGIN} + ${1}))
            ;;
            nint) #echo "BEGIN ${BEGIN} nint ${1}" >> /dev/stderr
                FROM=$((${BEGIN} - ${1:1}))
                TO=${BEGIN}
            ;;
            *) END=${BEGIN}
            ;;
        esac
    fi
    #echo "FROM ${FROM} - TO ${TO}" >> /dev/stderr

    [ ${FROM} -eq 1 ] && VIEW="yes"
    k=1
    for ((z=0;z<${TO};z++)) ; do
        [ ${k} -ge ${FROM} ] && [ ${k} -le ${TO} ] && VIEW="yes" || unset VIEW
        case $z in
            0) [ ! -z ${VIEW} ] && line_h
            ;;
            *)
                case ${LINES[${l=0}]:0:1} in
                    m) ((n=n+1))
                        [ ! -z ${VIEW} ] && line_m $(($l+2)) $n
                        ;;
                    o) ((p=p+1))
                        [ ! -z ${VIEW} ] && line_o $(($l+2)) $p
                        ;;
                    r) ((q=q+1))
                        [ ! -z ${VIEW} ] && line_r $(($l+2)) $q
                        ;;
                esac
             ((l=l+1))
        esac
        ((k=k+1))
    done
    return
}

findline()

Akceptuje jeden až dva parametry. První parametr $1 je vždy textový řetězec. Může to být zástupné jméno speciální sekvence, kterou lze dále parametrizovat parametrem $2, ale ne každá speciální sekvence ho vyžaduje. Např. sekvence \b, kterou lze vyhledat přes zástupné jméno backchar žádný další parametr nepotřebuje.

Naopak zástupné jméno color bez parametru $2 vyhledá pouze sekvenci [m. Pokud se má vyhledat sekvence [0m, kterou se vrací nastavení zvýraznění textu na výchozí hodnotum, nebo jiný typ zvýraznění textu, je potřeba použít ještě jeden parametr.

Pokud neznáme parametry barevného zvýraznění, které hledáme, můžeme si vypomoci tečkou. A pokud chceme vyhledat dva řetězce oddělené mezerou, musíme místo mezery použít zástupnou sekvenci \x20. Více viz akce find

findline () {
    [ ${DEBUG} ] && \
        echo "INFO: findline() - ${1} - ${2}" >> /dev/stderr
    local STRING

    STRING=$(get_string ${1} ${2} | ${SED} 's/\\/\\\\/g' | ${SED} 's/\[/\\\[/g')
    grep --color -n "${STRING}" ${RECORDFILE}
    [ ${DEBUG} ] && \
        echo "INFO: ${STRING}" >> /dev/stderr
    return
}

decolor()

Funkce odbarvuje proud dat

decolor () {
    sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g"
    return
}

delete_line()

Funkce maže řádky. Parametr $1 umožňuje dodatečnou korekci

delete_line () {
    [ ${DEBUG} ] && \
        echo "INFO: delete_line() ${LINE} - ${ENDLINE} correction ${1}" >> /dev/stderr

    case $(value ${1}) in
        inte|nint)correction by count lines
            if [ -z "${ENDLINE}" ] ; then
                ENDLINE=$((${LINE} + ${1}))
            else
                ENDLINE=$((${ENDLINE} + ${1}))
            fi
            ;;
        flow)add correction by time interval
            if [ -z "${ENDLINE}" ] ; then
                TIME=$(time_by_line ${LINE})
                [ ${DEBUG} ] && \
                    echo "INFO: delete_line() LINE time ${TIME} corrected by time ${1}" >> /dev/stderr
                ENDLINE=$(line_by_time $(float_from_string $((${TIME} + $(float_to_string ${1})))))
                [ $? -eq 1 ] && \
                    echo ${ENDLINE} && \
                        return 1
            else
                TIME=$(time_by_line ${ENDLINE})
                [ ${DEBUG} ] && \
                    echo "INFO: delete_line() ENDLINE time ${TIME} corrected by time ${1}" >> /dev/stderr
                ENDLINE=$(line_by_time $(float_from_string $((${TIME} + $(float_to_string ${1})))))
                [ $? -eq 1 ] && \
                    echo ${ENDLINE} && \
                        return 1
            fi
            ;;
        nflow)correction start interval selected by range
            if [ -z "${ENDLINE}" ] ; then
                TIME=$(time_by_line ${LINE})
                ENDLINE=${LINE}
                [ ${DEBUG} ] && \
                    echo "INFO: delete_line() range corrected from end by time ${1}" >> /dev/stderr
                LINE=$(line_by_time $((${TIME} - $(float_to_string ${1:1}))))
                [ $? -eq 1 ] && \
                    echo ${LINE} && \
                        return 1
            else
                TIME=$(time_by_line ${LINE})
                [ ${DEBUG} ] && \
                    echo "INFO: delete_line() range corrected from LINE time by time ${1}" >> /dev/stderr
                LINE=$(line_by_time $((${TIME} - $(float_to_string ${1:1}))))
                [ $? -eq 1 ] && \
                    echo ${LINE} && \
                    return 1
            fi
            ;;
    esac
    if [ -z "${ENDLINE}" ] ; then
        [ ${DEBUG} ] && \
            echo "INFO: delete_line() ${1} line ${LINE}" >> /dev/stderr
                #sed -n ${LINE}p ${RECORDFILE}
      ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
#.n${LINE}
#q
#EOF
        (
                ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
        ) >> ${TEMP_DIR}/delete_event
       jiná událost, jiný soubor
        EVENT=$(head -1 ${TEMP_DIR}/delete_event)
        echo ${EVENT} >> /dev/stderr
        anone "Its event from line ${LINE}, for delete push 'y' key"
        [ $? -eq 1 ] && \
            return
        ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE}d
w
q
EOF
    else
        [ ${DEBUG} ] && \
            echo "INFO: do_event() ${1} by scope from ${LINE} line to ${ENDLINE}" >> /dev/stderr
        [ ${ENDLINE} -lt $((${LINE} + 1)) ] && \
            echo "ERR: do_event() - Invalid scope for ${1} (${LINE} - ${ENDLINE})" >> /dev/stderr && \
            return 1
        sed -n ${LINE},${ENDLINE}p ${RECORDFILE}
        anone "Delete this scope ${LINE}-${ENDLINE}? For continue push 'y' key." >> /dev/stderr
        [ $? -eq 1 ] && \
            return
        ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE},${ENDLINE}d
w
q
EOF
    fi
    return
}

do_event()

Funkce zpracovává příkazy předané akci event. Ta vykonává jen takové operace, které nevyžadují posun časování událostí, kterých se příkaz bezprostředně netýká.

delete

$ asciiframe event ascii.cast delete LINE|TIME|RANG [CORRECTION]

Prvním parametrem příkazu se určuje číslo řádku, který se má smazat. Je doporučeno pracovat při odstraňování událostí s číslem řádku, protože asciicastu může obsahovat série událostí, které se spouští ve stejný čas. Nicméně u dlouhých záznamů lze použít i čas – buď v „human-ready” formátu [HH:]MM:SS, nebo v sekundách a CORRECTION.

Odstranění celého bloku řádků se totiž dělá přes rozsah (RANG), který má jednoduchý formát START-END. Následující ukázka demonstruje jak smazat události, počínaje hned tou první na řádku 2, včetně události na řádku 300:

$ asciiframe.sh event ascii.cast delete 2-300

Celkem bude odstraněno 298 řádků. Parametr CORRECTION to ale umožňuje změnit. Kladná hodnota do výběru řádky přidá, takže při následující kombinace by bylo odstraněno 308 řádků:

$ asciiframe.sh event ascii.cast delete 2-300 10

A záporná naopak ubere. Při nastavení rozsahu pořadím řádků tak nemá CORRECTION valný význam. Ovšem zcela jinak tomu bude, pokud budeme pracovat s časem.

$ asciiframe view asahi-m1.backup1 369
 369: 368: [569.433513, "o", "ls /mnt/"]
$ asciiframe view asahi-m1.backup1 569.4 10
[569.433513, "o", "ls /mnt/"]
 ...
[572.738149, "o", "\u001b[C /mnt/\b\b\b\b\b\b"]
This is content of scope 368-378. For continue push 'y' key

TODO!!! S časem to nefunguje jak má. Bere pouze jeden řádek a na CORRECTION sere

Pokud víme, že nás z půlhodinového záznamu zajímá jenom část, která proběhla někdy 12 a 15 minutou, můžeme nejprve nahrubo odstranit vše co je mimo tento interval, protože s kratším záznamem se bude líp pracovat. A provedeme to následující sekvencí příkazů:

$ tail ascii.cast
[2993.557207, "o", "exit\r\n"]
$ asciiframe.sh event ascii.cast time 2993.557207
00:49:53
$ asciiframe.sh event ascii.cast time 15:00
900.000
$ asciiframe view asahi-m1.backup 900.0 1
549: 548: [650.729441, "o", "\r...
               ...
       |       ora:~\u0007\u001b[?2004h[root@fedora ~]# "]
550: 549: [939.064954, "o", "w"]
$ asciiframe view ascii.cast -1
3578:3577: [2993.525216, "o", "\r\n\u001b[?2004l\r"]
$ asciiframe event ascii.cast delete 550-3578
$ asciiframe find ascii.cast enter
  ...
543:[646.480455, "o", "\u001b]0;root@fedora:~ ...
  ...
$ asciiframe clip ascii.cast 543 > clip.cast

dump

Příkaz odstraňuje z proudu dat sekvence barev. Používá se ke zpracování výstupu aplikace asciinema, který vysype na stdout když se použije příkaz cat. Při tom ale zmizí hlavička, ve které jsou metadata, co obsahují informace o velikosti terminálu v době pořízení záznamu, se kterými se pracuje během „přehrávání” asciicastu.

Pokud bude rozměr terminálu stejný, jako byl během nahrávání, a jeho buffer dost velký na to aby se vše do něj vešlo, můžeme si ho poslat přes rouru na less a procházet, jako by to byl obyčejný text:

$ asciinema cat ascii.cast | less -R

Parametr -R je nutný, protože bez něj by se zobrazily i terminálové sekvence, které řeší jeho obarvení, takže by výstup začínal nějak takto:

ESC[0mESC[49mESC[39mESC[27mE...

Pokud z něj tedy chceme vydolovat prostý text, je potřeba odstranit terminálové sekvence, které řeší jeho obarvení. A právě na to lze využít akci event a příkaz dump. Máme-li k dispozici původní ascii.cast, stačí jenom tohle:

$ asciiframe event ascii.cast dump > monochrome.raw

Ale pokud chceme odbarvit již existující RAW výstup, který nemusí být nutně výstupem aplikace asciinema, můžeme ho předat jako parametr

$ asciiframe event ascii.cast color.raw dump > monochrome.raw

text

Příkaz text lze použít dvojím způsobem. S parameterem, nebo bez něj. Je-li předán parametr, bude výsledek podobný, jako při akci find, ovšem s jedním, dost podstatným rozdílem, který lze demonstrovat jedině na praktické ukázce:

~$ asciiframe.sh find ascii.cast newline
...
287:[114.189237, "o", "\u001b[?2004l\r\r\n"]
...
~$ asciiframe.sh find ascii.cast text newline
...
   287   0.600320 : [114.189237, "o", "\u001b[?2004l\r\r\nexit\r"]
...
~$ asciiframe.sh event ascii.cast text newline
...
   287   0.600320 : [114.189237, "o", "\u001b[?2004l\r\r\nexit\r"]
...

Povšimněte si, že místo řetězce newline byla vyhledána sekvence, která se používá pro zalomení řádky, ale výsledek, zpracovaný přes akci event vrací poněkud jiný obsah události a také, kromě čísla řádku jedno číslo navíc.

Je to proto, že příkaz text je aplikován až na monochromatickou verzi asciicastu, která má sloučené jednoznakové události do celistvých řetězců. A číslo 0.600320 udává délku sloučeného intervalu. Chceme-li se tedy podívat, jak vypadají události v asciicastu, můžeme předat obě čísla akci ''#akce_view!view

~$ asciiframe view ascii.cast 287   0.600320
287: 286: [114.189237, "o", "\u001b[?2004l\r\r\n"]
288: 287: [114.289557, "o", "e"]
289: 288: [114.389557, "o", "x"]
290: 289: [114.489557, "o", "i"]
291: 290: [114.589557, "o", "t"]
292: 291: [114.689557, "o", "\r"]

Jak vidno, k události přibyl řetězec exit, složený z jednoznakových událostí. Můžeme si tedy rovnou vyzkoušet co vyhledá akce ''find:

~$ asciiframe.sh find ascii.cast exit
find – exit
~$ asciiframe.sh find ascii.cast text exit
find – exit
   287   0.600320 : [114.189237, "o", "\u001b[?2004l\r\r\nexit\r"]

Volání tohoto příkazu přes akci find však má své omezení – vyžaduje řetězec. Pokud ale zavoláme příkaz text přes akci event bez parametru, vypíše se celý asciicast, který můžeme prohnat následujícím filtrem a uložit jako nový, monochromatický asciicast, vhodný k vytvoření plain textového záznamu:

~$ asciiframe.sh event ascii.cast text | cut -c 21- > monochrome.cast
~$ asciiframe.sh play monochrome.cast

Z tohoto záznamu budou odstraněny veškeré vizuální efekty, překlepy, zvýraznění i barvy. Na druhou stranu lze všechny události takto vyčištěného asciicastu rozdělit příkazem split a následně podle potřeby obarvit.

split

Když se podíváte do asciicastu, je ihned vidět, kdy šel na terminál výstup z klávesnice a kdy byl obsah zkopírován ze schránky, nebo ho vypsala nějaká aplikace. Při psaní se totiž ukládá každý zapsaný znak jako samostatná událost, která má svůj čas. A split umožňuje takovou sérii událostí vygenerovat. Buď rozdělením stávající události, nebo rozdělením obsahu komentáře, vloženého na konkrétní čas. Při sledování záznamu bude výsledek stejný, jako by se text, který byl původně na příkazovovou řádku vložen ze schránky, zapisoval.

Rozdělení lze provést jednoduše:

~$ asciiframe.sh event ascii.cast split 20

Aby to působilo přirozeně, je ovšem potřeba zvolit optimální interval. V dokumentaci funkce playcast(), je ukázkový příklad využití příkazu context, který můžete aplikovat i na svůj vlastní asciicast, ze kterého plyne, že se rychlost psaní jednotlivých znaků pohybuje zhruba od 0.1 do 0.3 sekundy. Je tedy pouze na vás, jestli upřednostníte výchozí interval 0.1 (default), nebo použijete jinou hodnotu, ideálně v rozmezí od 0.1 do 0.3 sekundy. Viz příklad:

~$ asciiframe.sh event ascii.cast split 20 0.25

Každopádně ještě než dojde na realizaci příkazu split, budete dotázáni, zda se má příkaz opravdu realizovat, nebo ne. Tohle výchozí chování skriptu je ale možné potlačit volbou -f – do souboru asciicastu se změny zapíšou jen když se poslední událost vejde do intervalu zobrazení, který následuje za řádkem č. 20.

merge

TODO

~$ asciiframe.sh event ascii.cast merge 214 216

Všechny operace Pokud tedy chcete vložit komentář, a tedy potřeba zkrátit, nebo naopak prodloužit interval zobrazení, je potřeba nebo časo žádnou dělají žádnérealizují jednorázové operace, při kterých se nepracuje s časem událostí. kterých se to týká.

vždy a pouze jedné událost. umožňuje vložit novou, nebo naopak odstranit stávající událost do/z asciicastového souboru Klíčové je, že u těchto operací nedochází k posunu časování u ostatních událostí.

Je-li přidaná nová událost, nastavuje se její čas buď podle parametru TIME, nebo podle aktuálního času události na řádku LINE.

Přes parametr CONTENT se zadává obsah události, který je v některých případech (jako např. při události typu resize) dokonce povinný.

decolor

 ~$ asciiframe.sh event ascii.cast decolor

Vypíše na standardní výstup asciicast, ze kterého jsou odstraněny sekvence, jimiž se nastavují barvy a jiná zvýraznění.

Příkaz zavolá funkci print_monochrome(), která zpracuje asciicast ve dvou průchodech, při kterých zobrazované události „odbarvuje” funkcí event_plain(). Ty dva průchody jsou nutné, protože formát asciicastu neřeší to, zda-li jsou sekvence speciálních znaků rozdělené mezi dvě po sobě následující události, nebo ne. Jejich iterval je totiž většinou tak krátký, že ho lidské oko nepostřehne a terminálu, který pracuje s proudem dat nevadí, že je sekvence takto rozdělená.

\u0007 - zalomení řádku. Vložit jako samostatnou událost
\u001b]0; - reset řádku ideální bod pro první událost
\u001b[H\u001b[2J\u001b[3J - reset okna

dump

Příkazem lze odbarvit RAW, produkovaný aplikací asciinema

pause

Příkaz mění interval zobrazení události identifikované řádkem tím, že upraví – podle předaného poměru její interval vůči intervalu zobrazení předchozí události. Nedělá tedy žádné změny v časování následujících událostí, jako to dělá akce clip.

 ~$ asciiframe.sh event ascii.cast pause 10 1

Nastaví čas události z řádku č. 10 tak, aby se zobrazila bezprostředně za předchozí událostí, která je na řádku č. 9. Tím se její čas zobrazení zvětší prakticky o celý její interval zobrazení. Což ničemu nevadí, protože na terminálu budou zobrazeny obě dvě. Naopak v následujícím případě se posune těsně za událost následující, což prodlouží interval, než se zobrazí.

 ~$ asciiframe.sh event ascii.cast pause 10 0

Pokud chceme její interval zobrazení pouze zkrátit, lze použít číslo v rozmezí 0.0001 až 0.9999. Při hodnotě 0.5, se nastaví čas tak, že budou oba intervaly stejně dlouhé.

Tímto způsobem lze postupně posunout časy všech událostí, aniž by bylo nutné použít akci clip a tím vytvořit časový prostor pro akci split, pokud chceme rozdělit pouze jednu událost.

do_event()
{
    [ ${DEBUG} ] && \
        echo "INFO: ${1} - ${2} - ${3}" >> /dev/stderr
    local COMMAND
    local CONTEXT
    local EVENT
    local INDEX
    local ITEM
    local NEXT
    local PREV
    local TEST
    local TIME

    case "${1}" in
        decolor|\
        delete|\
        merge) COMMAND=${1}
            [ ${DEBUG} ] && \
                echo "INFO: do_event: ${1} - ${2} - ${3}" >> /dev/stderr
            [ ${COMMAND} == "decolor" ] && \
                [ -z "${2}" ] && \
                    print_monochrome && \
                        return
            [ ${COMMAND} == "delete" ] && \
                [ -z "${2}" ] && \
                    help event_delete && \
                        return 1
            [ ${COMMAND} == "merge" ] && \
                [ -z "${2}" ] && \
                    help event_merge && \
                        return 1
            case $(value ${2}) in
                inte|nint) LINE=$(line_by_inte ${2})
                    [ $? -eq 1 ] && \
                        echo ${LINE} && \
                        return 1
                    [ ${DEBUG} ] && \
                        echo "INFO: do_event() ${1} line ${LINE}" >> /dev/stderr
                    shift && shift
                ;;
                flow|nflo|time) LINE=$(line_by_time ${2})
                    [ $? -eq 1 ] && \
                        echo ${LINE} && \
                        return 1
                    [ ${DEBUG} ] && \
                        echo "INFO: do_event() ${1} line ${LINE} by time ${2}" >> /dev/stderr
                    shift && shift
                ;;
                rang) TEST=(${2/-/ })
                    LINE=$(line_by_inte ${TEST[0]})
                    [ $? -eq 1 ] && \
                        echo ${LINE} && \
                        return 1
                    ENDLINE=$(line_by_inte ${TEST[1]})
                    [ $? -eq 1 ] && \
                        echo ${ENDLINE} && \
                        return 1
                    [ ${DEBUG} ] && \
                        echo "INFO: do_event() ${1} by scope from ${LINE} to ${ENDLINE}" >> /dev/stderr
                    shift && shift
                ;;
                stri) echo TODO-delete-string
                    return 1
                ;;
            esac
            case ${COMMAND} in
                decolor) event_decolor ${1}
                    ;;
                delete) if [ -z "${1}" ] ; then
                    [ ${DEBUG} ] && \
                        echo "INFO: do_event() go to ${1}" >> /dev/stderr
                        delete_line
                    else
                        delete_line ${1}
                    fi
                    ;;
                merge) sed -n ${LINE},${ENDLINE}p ${RECORDFILE} | events_merge
                        echo ${MESSAGE} >> /dev/stderr
                    ;;
            esac
            return
        ;;
        dump) [ ${DEBUG} ] && \
                echo "INFO: do_event: ${1} - ${2}" >> /dev/stderr
            case $(value ${2}) in
                file) cat ${2} | decolor
                    ;;
                *) ${ASCIINEMA} cat ${RECORDFILE} | decolor
                ;;
            esac
        ;;
        pause)Při hodnotě 0 posune dopředu, těsně za následující událost
           Při hodnotě 1 posune dozadu, těsně za předchozí událost
            case $(value ${2}) in
                inte|nint) LINE=$(line_by_inte ${2})
                ;;
                time|nflo|flow) LINE=$(line_by_time ${2})
                ;;
            esac
            if [ -z "${3}" ] ; then
                get_view context ${LINE}
                echo "Missing parameter. 1 move event to before item time and 0 to next. Value 0.5 set to middle" >> /dev/stderr
                while true ; do
               read -p "(ANO/NE) : " yn
                read -er yn
                case $yn in
                    0|1|0.[0-9]*) case $yn in
                            1) POMER="1000000"
                                ;;
                            0) POMER="0"
                                ;;
                            *) POMER=$(float_to_string ${yn})
                                ;;
                        esac
                        break
                        ;;
                    [NnQqCc]*)N - no, Q - quit, C - cancel, S - storno
                        return 1
                        ;;
                    *) echo "For interruption write s, q or c. For continue must be value 0, 1 or float as 0.5" >> /dev/stderr ;;
                    esac
                done
            fi
            case $(value ${3}) in
                flow) POMER=$(float_to_string ${3})
                ;;
                inte) case ${3} in
                    1) POMER="1000000"
                        ;;
                    0) POMER="0"
                        ;;
                    *) echo "ERROR: do_event() ${1} - Accepted 0, 1 or float as 0.5 not ${2}" >> /dev/stderr
                        return 1
                        ;;
                    esac
                ;;
            esac
            if [ "${POMER}" -lt "999999" ] && [ "${POMER}" -gt "0" ] ; then
                #echo "Dělím interval řádku ${LINE}"
                INTERVAL=$(get_view context ${LINE})
                INTERVAL=$(float_to_string ${INTERVAL%% *})
                TIME=$(( $(time_by_line $((${LINE} - 2)) ) + ${INTERVAL} -
                $((${INTERVAL} * ${POMER} / 1000000))
                ))
            elif [ "${POMER}" -eq "1000000" ] ; then
                echo "Nastavit čas těsně před předcházející událost"
                TIME=$(( $(time_by_line $((${LINE} - 2))) + 1 ))
            elif [ "${POMER}" -eq "0" ] ; then
                echo "Nastavit čas těsně před následující událost"
                TIME=$(( $(time_by_line ${LINE}) - 1 ))
            else
                echo "ERROR: do_event() ${1} - Value ${2} is not acceptable" >> /dev/stderr
                return 1
            fi
            (
                ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
            ) >> ${TEMP_DIR}/time_event
           jiná událost, jiný soubor
            EVENT=$(head -1 ${TEMP_DIR}/time_event)
            TIME=$(float_from_string ${TIME})
            echo ${EVENT} >> /dev/stderr
            anone "Change time this event from line ${LINE} to ${TIME}?"
            [ $? -eq 1 ] && \
                return
           change time
            ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
.s/[^,]*\,/[${TIME},/1
w
q
EOF
            return
        ;;
        replace)nahradí řetězec jiným
            echo TODO-event-replace
        ;;
        split)
            case $(value ${2}) in
                inte) split_events ${2} ${3}
                ;;
            esac
        ;;
        text)Pro vyhledání řetězce rozděleného mezi více událostí
           je potřeba zbavit text obarvení, zalomení a podobných
           blbostí, pak scelit jednoznakové sekvence.
           Je taky nutné ošetřit přes sed zpětná lomítka
            if [ -z "${2}" ] ; then
                print_monochrome | ${SED} 's/\\/\\\\/g' | events_merge
            else
                print_monochrome | ${SED} 's/\\/\\\\/g' | events_merge | grep --color $(get_string ${2})
            fi
            return
        ;;
        time)
            if [ ${3} ] ; then
                case $(value ${3}) in
                    inte)one event
                       čas může být stejný jako před za
                        LINE=${3}
                        PREV=$(time_by_line $((${3} - 1 )) )
                        NEXT=$(time_by_line ${3})
                        [ ${DEBUG} ] && \
                            echo "INFO: do_event() ${1} ${3} from ${PREV} to ${NEXT}" >> /dev/stderr
                    ;;
                    rang)more events
                        TEST=(${3/-/ })
                        LINE=$(line_by_inte ${TEST[0]})
                        [ $? -eq 1 ] && \
                            echo ${LINE} && \
                            return 1
                        PREV=$(time_by_line $((${LINE} - 1 )) )
                        ENDLINE=$(line_by_inte ${TEST[1]})
                        [ $? -eq 1 ] && \
                                echo ${ENDLINE} && \
                                return 1
                        NEXT=$(time_by_line ${ENDLINE})
                        [ ${DEBUG} ] && \
                            echo "INFO: do_event() ${1} ${3} from ${PREV} to ${NEXT}" >> /dev/stderr
                    ;;
                    *) echo "ERROR: do_event() ${1} require LINE or RANG" >> /dev/stderr
                        return 1
                    ;;
                esac
                case $(value ${2}) in
                    inte) TIME=$(time_by_line ${2}) ;;
                    flow) TIME=$(float_to_string ${2}) ;;
                    time) TIME=$(float_to_string $(print_seconds ${2})) ;;
                esac
                if [ "${TIME}" -ge "${PREV}" ] && [ "${TIME}" -le "${NEXT}" ] ; then
                    TIME=$(float_from_string ${TIME})
                   výkonná funkce
                    same_time
                else
                    echo "ERROR: do_event() ${1} - ${2} is out of scope $(float_from_string ${PREV}) - $(float_from_string ${NEXT})" >> /dev/stderr
                    return 1
                fi
            else
                if [ "${2//:/}" == "${2}" ] ; then
                    print_time ${2}
                else
                    print_seconds ${2}
                fi
            fi
        ;;
    esac
    [ ${DEBUG} ] && \
        echo "INFO:do_event() $1:$2:$3" >> /dev/stderr
    return
}

do_merge()

TODO

do_merge () {
   projede asciicast a sloučí jednoznakové eventy se stejným časem
    [ ${DEBUG} ] && \
        echo "INFO: splittext() ${1} - ${2} - ${3}" >> /dev/stderr
    local line
    local MESS
    local TIME

    x=0
    ${JQ} -c '[.[] ] | .[2] | tostring' ${RECORDFILE} | sed 's/\\/\\\\/g' | while read line ; do
        ((x++))
        [ $x -eq 1 ] && [ -z "${1}" ] && sed -n 1p ${RECORDFILE} && continue
        if [ -z "${1}" ] ; then
            echo >> /dev/null
            case ${#line} in
                3) if [ -z "${BEFORE}" ] ; then
                        BEFORE="${LINES[$((x-2))]}"
                        MESS="${line:1}"
                    else
                        if [ "${BEFORE}" == "${LINES[$((x-2))]}" ] ; then
                            MESS="${MESS::-1}${line:1}" >> /dev/stderr
                        else
                            echo "[$(float_from_string ${BEFORE:1}), \"o\", \"${MESS::-1}\"]"
                            BEFORE="${LINES[$((x-2))]}"
                            MESS="${line:1}"
                        fi
                    fi
                ;;
                *) if [ -z "${MESS}" ] ; then
                        echo >> /dev/null
                    else
                        echo "[$(float_from_string ${BEFORE:1}), \"o\", \"${MESS::-1}\"]"
                        BEFORE=''
                        MESS=''
                    fi
                    case ${LINES[$((x-2))]:0:1} in
                        m|r) sed -n ${x}p ${RECORDFILE}
                        ;;
                        *) echo "[$(float_from_string ${LINES[$((x-2))]:1}), \"o\", ${line}]"
                        ;;
                    esac
                ;;
            esac
        else
            echo param >> /dev/stderr
        fi
    done
    return
}

do_set()

vložení loga: print logo, které je přes více řádků... za každým je zalomit \u001b ECMAScript6 \033 POSIX (oktál) \01b (hexa) ESC[ Přepnutí x termu na alternativní screen ?1049h a skok zpátky ?1049l xterm má privátní módy 1047,1048 a 1049 Přepnutí ?1h a 1B= jsou pro VT100 pak je sekvence pro clear H-2J-3J

TODO

breakline

Vloží do asciicastu sekvenci, která zalomí text a pokračuje ve výpisu na dalším řádku. Pokud se vloží tato událost mezi jednoznakové události, které vypisují text na obrazovku, lze ji přidat bez dalšího textu. Takto:

~$ asciiframe.sh set ascii.cast breakline 120

Ale můžeme využít tuhle událost i k vložení pokračování textu komentáře, který je přes celou šířku terminálu

color

TODO

~$ asciiframe.sh set ascii.cast color 120 "1;39"

Vloží sekvenci, která zapne tučný bílý text

~$ asciiframe.sh set ascii.cast color 123 reset

Vypne veškeré zvýraznění

marker

TODO

resize

TODO

line

Příkaz umožňuje měnit typ události identifikované číslem řádku:

~$ asciiframe.sh view ascii.cast m
  ...
10:   4: [2.580000, "m", "# 4"]
 ~$ asciiframe.sh set ascii.cast line 10 o
 ~$ asciiframe.sh view ascii.cast 10
10:   6: [2.580000, "o", "# 4"]

Toto je ukázkový příklad, jak změnit událost typu m (marker) na zobrazitelnou událost. Stejným způsobem lze změnit naopak událost typu o na marker, pomocí příkazu marker jí změnit obsah a poté přepnout, tak, aby z ní byla opět zobrazitelná událost

TODO - sjednotit syntaxi příkazů

do_set () {
   projede asciicast a sloučí jednoznakové eventy se stejným časem
    [ ${DEBUG} ] && \
        echo "INFO: do_set() COMMAND ${1} TARGET ${2} CONTENT ${@}" >> /dev/stderr
    local i
    local x
    local EVENTS
    local INDEX
    local MESSAGE
    local LINE
    local TIME
    local TEST

    case "${1}" in
        breakline|color|clear|comment|commentline|root|user|newline) case $(value ${2}) in
                inte) LINE=$(line_by_inte ${2})
                    [ $? -eq 1 ] && \
                        echo ${LINE} && \
                        return 1
                    TIME=$(time_by_line $((${LINE} - 1)))
                    TIME=$(float_from_string $(( $(( $((
                        $(time_by_line ${LINE}) - ${TIME}
                        )) / 2 )) + ${TIME} )) )
                    ;;
                time) TIME=$(float_strip $(print_seconds ${2}))
                    ;;
                flow) TIME=$(float_unify ${2})
                    ;;
            esac
           sestavení řetězce vkládaného na čas ${TIME}
            case ${1} in
                breakline) shift ; shift
                    STRING="$(get_string breakline)${@}"
                    ;;
                clear) shift ; shift
                   clear command effect
                    STRING="\u001b$(get_string color 0)"
                    STRING="${STRING}\u001b$(get_string H)"
                    STRING="${STRING}\u001b$(get_string J 2)"
                    ;;
                clear) shift ; shift
                   set color or another change of text properties
                    STRING="\u001b$(get_string color ${@})"
                    ;;
                comment) shift ; shift
                   komentář kontroluje aktuální šířku terminálu
                    for ((x=0;x<LINE;x++)) ; do
                        [ ${LINES[$x]:0:1} == "r" ] && RESIZE=$(($x - 1))
                    done
                    if [ ${RESIZE} ] ; then
                        SIZE=($(get_resize ${RESIZE}))
                        WIDTH=${SIZE[1]}
                    else
                        WIDTH=$(head -1 ${TEMP_DIR}/width)
                    fi
                    STRING="$(get_string newline)"
                    STRING="${STRING}\u001b$(get_string cursor-show 2004)"
                    STRING="${STRING}\r"
                    STRING="${STRING}\u001b$(get_string cursor-hide 2004)"
                   zkráceno podle aktuální šířky terminálu
                    TEST="${@}"
                    STRING="${STRING}> ${TEST:0:$((${WIDTH} - 2))}"
                    STRING="${STRING}"
                    ;;
                commentline) shift ; shift
                   break a long string
                    TEST="${@}"
                    if [ -z "${TEST}" ] ; then
                        STRING="> "
                    else
                       komentář kontroluje aktuální šířku terminálu
                        for ((x=0;x<LINE;x++)) ; do
                            [ ${LINES[$x]:0:1} == "r" ] && RESIZE=$(($x - 1))
                        done
                        if [ ${RESIZE} ] ; then
                            SIZE=($(get_resize ${RESIZE}))
                            WIDTH=${SIZE[1]}
                        else
                            WIDTH=$(head -1 ${TEMP_DIR}/width)
                        fi
                        STRING="$(get_string breakline)${@}"
                       zkráceno podle aktuální šířky terminálu
                        STRING="$(get_string newline)> ${TEST:0:$((${WIDTH} - 2))}"
                    fi
                    ;;
                newline) shift ; shift
                   newline after event
                    STRING="${@}$(get_string newline)"
                    ;;
                root) shift ; shift
                    STRING="\u001b$(get_string enter)"
                    STRING="${STRING}root@${@}: ~"
                    STRING="${STRING}$(get_string breakline)#root@{@}:~# "
                    ;;
                user) shift ; shift
                    STRING="\u001b$(get_string enter)"
                    STRING="${STRING}${@}: ~"
                    STRING="${STRING}$(get_string breakline)${@}:~$ "
                    ;;
            esac
            for i in ${!LINES[@]} ; do
                if [ ${LINES[i]:1} -ge $(float_to_string ${TIME}) ] ; then
                LINE=$((${i} + 2))
                (
                    ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
                ) >> ${TEMP_DIR}/new_event
               jiná událost, jiný soubor
                EVENT=$(head -1 ${TEMP_DIR}/new_event)
                echo ${EVENT} >> /dev/stderr
                TEXT="Set new event with time ${TIME}"
                TEXT="${TEXT} before this event from line ${LINE}?"
                anone "${TEXT}"
                [ $? -eq 1 ] && return
                ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE}p
i
[${TIME}, "o", "${STRING}"]
.
w
q
EOF
                    return
                fi
            done
            if [ ${TIME} ] ; then
                anone "Add as a new event to end of file?"
                [ $? -eq 1 ] && return
                ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
$p
a
[${TIME}, "o", "${STRING}"]
.
w
q
EOF
                return
            fi
            return 1
        ;;
        duration)Nastavení duration podle poslední události
            case ${3} in
                inte)microseconds 84000000
                    DURATION=${3}
                ;;
                flow)time in seconds 84.0
                    DURATION=$(float_from_string ${3})
                ;;
                time)human time as 1:24
                    DURATION=$(print_seconds ${3})
                ;;
                *)set duration by time of the last event
                    DURATION=${LINES[-1]:1}
                ;;
            esac
            float_strip $(float_from_string ${DURATION}) > ${TEMP_DIR}/duration
            TEST=$(header_print)
            ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
1i
${TEST}
.
2d
w
q
EOF
        ;;
        marker)
            case $(value ${2}) in
                inte) ORDER=${2}
                    x=0
                    for i in ${!LINES[@]} ; do
                        [ "${LINES[$i]:0:1}" == "m" ] && \
                            ((x++)) && \
                            echo "$x ${LINES[$i]}" >> /dev/stderr
                     [ "${LINES[$i]:0:1}" == "m" ] && \
                        ((x+1)) && \
                        [ ${x} == ${ORDER} ] && \
                            TIME=$(float_from_string ${LINES[$i]:1}) && \
                            break
                    done
                    echo ${TIME} >> /dev/stderr
                    [ $i -eq ${#LINES[@]} ] && \
                        echo "Marker with order ${ORDER} not exists" && \
                            return 1
                    ;;
                time) TIME=$(print_seconds ${2})
                    ;;
                flow) TIME=${2}
                    ;;
            esac
            [ -z "${TIME}" ] && echo "ERROR: do_set() ${1} - ${RECORDFILE} has count of markers less than ${2}" && \
                return 1
            shift && shift && MESSAGE="${@}"
            [ ${DEBUG} ] && \
                echo "INFO: do_set() call set_marker() - TIME ${TIME} - ${MESSAGE}" >> /dev/stderr
            set_marker ${TIME} "${MESSAGE}"
        ;;
        resize)
            case $(value ${2}) in
                inte) ORDER=${2}
                    x=0
                    for i in ${!LINES[@]} ; do
                        [ "${LINES[$i]:0:1}" == "r" ] && \
                            ((x++)) && \
                            echo "$x ${LINES[$i]}" >> /dev/stderr
                     [ "${LINES[$i]:0:1}" == "m" ] && \
                        ((x+1)) && \
                        [ ${x} == ${ORDER} ] && \
                            TIME=$(float_from_string ${LINES[$i]:1}) && \
                            break
                    done
                    echo ${TIME} >> /dev/stderr
                    [ $i -eq ${#LINES[@]} ] && \
                        echo "Resize with order ${ORDER} not exists" && \
                            return 1
                    ;;
                time) TIME=$(print_seconds ${2})
                    ;;
                flow) TIME=${2}
                    ;;
            esac
            [ -z "${TIME}" ] && echo "ERROR: do_set() ${1} - ${RECORDFILE} has count of resize events less than ${2}" && \
                return 1
            shift && shift && MESSAGE="${@}"
            if ${MESSAGE} =~ [[:digit:x:digit: ]] ; then
                [ ${DEBUG} ] && \
                    echo "INFO: do_set() TIME ${TIME} - ${MESSAGE}" >> /dev/stderr
                    set_resize ${TIME} "${MESSAGE}"
                else
                    echo "ERROR: do_set() ${1} - Invalid format ${MESSAGE}" >> /dev/stderr
                return 1
            fi
        ;;
        line) case $(value ${2}) in
            inte) [ ${DEBUG} ] && \
                    echo "INFO: do_set() ${1} set line ${2} to type ${3}" >> /dev/stderr
                case ${3} in
                    m|o)
                        (
                        ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${2}p
q
EOF
                        ) >> ${TEMP_DIR}/before_event
                       jiná událost, jiný soubor
                        EVENT=$(head -1 ${TEMP_DIR}/before_event)
                        echo ${EVENT} >> /dev/stderr
                        anone "Set this event from line ${2} to type \"${3}\"?"
                        [ $? -eq 1 ] && \
                            return
                       change type
                        ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${2}s/\"[mo]\"/\"${3}\"/
.
w
q
EOF
                        ;;
                    esac
                ;;
            esac
        ;;
        size)změna výchozí velikosti terminálu
            [ ${DEBUG} ] && \
                echo "INFO: do_set() ${2} - ${@}" >> /dev/stderr
                shift
            WSIZE=${@}
            if ${WSIZE} =~ [[:digit:x:digit: ]] ; then
                [ ${DEBUG} ] && \
                    echo "INFO: do_set() default size ${@}" >> /dev/stderr
                echo ${WSIZE#*x} > ${TEMP_DIR}/height
                echo ${WSIZE%x*} > ${TEMP_DIR}/width
            else
                echo "ERROR: do_set() ${1} - Invalid size format (WIDTHxHEIGHT): ${@}" >> /dev/stderr
                return 1
            fi
            TEST=$(header_print)
            #echo ${TEST} >> /dev/stderr
            ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
1i
${TEST}
.
2d
w
q
EOF
            return
        ;;
        title)Editace title asciicastu
            [ ${DEBUG} ] && \
                echo "INFO: do_set() ${2} - ${@}" >> /dev/stderr
            shift
            echo "${@}" > ${TEMP_DIR}/title
            TEST=$(header_print)
            #echo ${TEST} >> /dev/stderr
            ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
1i
${TEST}
.
2d
w
q
EOF
        ;;
        *) [ ${HELP} ] && help ${1} || help
        ;;
    esac
    return
}

same_time()

Výkonná funkce příkazu time, která přebírá nastavené proměnné LINE, ENDLINE a TIME a zapisuje do souboru události, co mají nastaven stejný čas - tím pádem se zobrazí ve stejný okamžik.

Rozsah těchto událostí lze vyhledat přes scope (akce view) a změnit jejich časování lze pomocí příkazu pause

same_time () {
    [ ${DEBUG} ] && \
        echo "INFO: same_time() - LINE ${LINE} ENDLINE ${ENDLINE} ${TIME}" >> /dev/stderr

    if [ -z "${ENDLINE}" ] ; then
        [ ${DEBUG} ] && \
            echo "INFO: same_time() ${1} line ${LINE}" >> /dev/stderr
        (
                ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
        ) >> ${TEMP_DIR}/same_event
       jiná událost, jiný soubor
        EVENT=$(head -1 ${TEMP_DIR}/same_event)
        echo ${EVENT} >> /dev/stderr
        anone "Change time of this event from line ${LINE} to ${TIME}? For continue push 'y' key"
        [ $? -eq 1 ] && \
            return
       change time
        ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
.s/[^,]*\,/[${TIME},/1
w
q
EOF
    else
        [ ${DEBUG} ] && \
            echo "INFO: same_time() ${1} by scope from ${LINE} line to ${ENDLINE}" >> /dev/stderr
        [ ${ENDLINE} -lt $((${LINE} + 1)) ] && \
            echo "ERROR: same_time() - Invalid scope for ${1} (${LINE} - ${ENDLINE})" >> /dev/stderr && \
            return 1
        sed -n ${LINE},${ENDLINE}p ${RECORDFILE}
        anone "Set for this scope ${LINE}-${ENDLINE} time to ${TIME}? For continue push 'y' key." >> /dev/stderr
        [ $? -eq 1 ] && \
            return
        ${ED} "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE},${ENDLINE}s/[^,]*\,/[${TIME},/1
w
q
EOF
    fi
    return
}

sm()

Funkce přebere řetězec s obsahem zobrazované události a následně vrací na znaky a speciální sekvence. Takže místo řetězce...

"\u001b[?2004h\u001b]0;user@stroj:~$"

vrátí sérii..

"
\u001b[?2004h
\u001b]0;
u
s
e
r
@
s
t
r
o
j
:
 
~
$
"

Kterou lze dále zpracovat

sm() {
    [ ${DEBUG} ] && echo "INFO: sm() ${#@}" >> /dev/stderr

    local BARVA
    local BEGIN
    local END
    local SPLIT

    SPLIT="${@}"
    BEGIN=0
    END=${#SPLIT}
    for ((i=BEGIN;i<END;i++)); do
        case ${SPLIT:${i}:1} in
        ' ') echo ' '&& i=$((i+1))
        ;;
        '*') echo '&asterisk;'&& i=$((i+1))
        ;;
        '.') echo '˙'&& i=$((i+1))
        ;;
        '\') case ${SPLIT:${i}:2} in
                '\\'|\
                '\"'|\
                '\b'|\
                '\n'|\
                '\r'|\
                '\t') echo ${SPLIT:${i}:2} && i=$((i+1))
                ;;
                '\u') case ${SPLIT:${i}:6} in
                        '\u001b')
                            case ${SPLIT:${i}:7} in
                                '\u001b]') case ${SPLIT:${i}:9} in
                                        '\u001b]'[0-9]';') echo ${SPLIT:${i}:9} && i=$((i+8))
                                            ;;
                                        esac
                                    ;;
                                '\u001b[') i=$((i+7))
                                        case ${SPLIT:${i}:1} in
                                            '?')
                                                i=$((i+1))
                                                case ${SPLIT:${i}:5} in
                                                    [1-2][0-9][0-9][0-9][lhsr])
                                                        echo "\u001b[?${SPLIT:${i}:5}" && \
                                                        i=$((i+4))
                                                    ;;
                                                esac
                                                case ${SPLIT:${i}:4} in
                                                    [0-9][0-9][0-9][lhsr])
                                                        echo "\u001b[?${SPLIT:${i}:4}" && \
                                                        i=$((i+3))
                                                    ;;
                                                esac
                                                case ${SPLIT:${i}:3} in
                                                    [0-9][0-9][lhsr])
                                                        echo "\u001b[?${SPLIT:${i}:3}" && \
                                                        i=$((i+2))
                                                    ;;
                                                esac
                                                case ${SPLIT:${i}:2} in
                                                    [0-9][lhsr])
                                                        echo "\u001b[?${SPLIT:${i}:2}" && \
                                                        i=$((i+1))
                                                    ;;
                                                esac
                                            ;;
                                            [0-9])
                                               AA - posun kurzoru nahoru o počet řádek
                                               B - posun kurzoru dolů o počet řádek
                                               C - posun kurzoru kupředu o N sloupců (doprava)
                                               D - posun kurzoru zpět (doleva) o N sloupců
                                               BARVA - test sekvence pro zvýraznění
                                               TAB
                                               REC
                                               HAC - posun kurzoru na pozici L;C
                                               JOKL
                                               EL
                                               HAKL
                                                NEXT=${SPLIT:${i}}
                                                AA="${NEXT%%A*}A"
                                                BARVA="${NEXT%%m*}m"
                                                TAB="${NEXT%%t*}t"
                                                REC="${NEXT%%r*}r"
                                                HAC="${NEXT%%H*}H"
                                                JOKL="${NEXT%%J*}J"
                                                EL="${NEXT%%l*}l"
                                                HAKL="${NEXT%%h*}h"
                                                [ -z "${AA//[0-9A]/}" ] && echo "\u001b[${AA}" && i=$((i+${#AA}-1))
                                                [ -z "${BARVA//[0-9m\\;\[]/}" ] && echo "\u001b[${BARVA}" && i=$((i+${#BARVA}-1))
                                                [ -z "${TAB//[0-9t\\;\[]/}" ] && echo "\u001b[${TAB}" && i=$((i+${#TAB}-1))
                                                [ -z "${REC//[0-9r\\;\[]/}" ] && echo "\u001b[${REC}" && i=$((i+${#REC}-1))
                                                [ -z "${HAC//[0-9H\\;\[]/}" ] && echo "\u001b[${HAC}" && i=$((i+${#HAC}-1))
                                                [ -z "${JOKL//[0-9J\\;\[]/}" ] && echo "\u001b[${JOKL}" && i=$((i+${#JOKL}-1))
                                                [ -z "${EL//[0-9l]/}" ] && echo "\u001b[${EL}" && i=$((i+${#EL}-1))
                                                [ -z "${HAKL//[0-9h]/}" ] && echo "\u001b[${HAKL}" && i=$((i+${#HAKL}-1))
                                               Rozseknutá barva
                                                TEST="${SPLIT:${i}:-1}"
                                                [ -z "${TEST//[0-9]\\;ub\[]/}" ] && echo "${TEST}" && i=$((i+${#TEST}-1))
                                            ;;
                                            A|H|J|K|m) echo "\u001b[${SPLIT:${i}:1}"
                                            ;;
                                            *)
                                            ;;
                                         esac
                                    ;;
                                '\u001b(') echo ${SPLIT:${i}:8} && i=$((i+7))
                                    ;;
                                '\u001b"') echo ${SPLIT:${i}:6} && i=$((i+5))
                                    ;;
                                '\u001b'[0-9]|\
                                '\u001b='|\
                                '\u001b>') Záznam 442750.cast – https://github.com/zechris/asciinema-rec_script
                                           obsahuje sekvence
                                           \u001b7 (0.030350) a
                                           \u001b8 (0.388992) obsahuje 
                                           a také rozdělenou sekvenci
                                           pro nastavení barvy mezi
                                           událostí spouštěnou v 0.403062 a
                                           událostí v čase 0.403087
                                           
                                        echo ${SPLIT:${i}:7} && i=$((i+6))
                                    ;;
                            esac
                        ;;
                        '\u0007') echo ${SPLIT:${i}:6} && i=$((i+5))
                        ;;
                    esac
                ;;
                *) echo "Neošetřeno ${SPLIT:${i}:2}"
                ;;
            esac
        ;;
        *) echo "${SPLIT:${i}:1}"
        ;;
        esac
    done
    return
}

set_marker()

Značky typu m se přidávají před události typu o ale tahle funkce je vyhledává od začátku souboru, takže pokud se stane, že se v souboru vyskytnout dvě (a více) událostí tohoto typu, těsně za sebou ve stejném čase bude druhotně editována jen značka, která je na první pozici.

Jak se to stane?

$ cat ascii.cast
...
[2.517264, "o", "t"]
[3.372158, "o", "\r\n"]
...
$ asciiframe.sh set 2.6 marker '# 1'
$ asciiframe.sh set 3.373 marker '# 2'
$ cat ascii.cast
...
[2.517264, "o", "t"]
[2.6, "m", "# 1"]
[3.372158, "o", "\r\n"]
[3.373, "m", "# 2"]
...
$ asciiframe view demo.cast m
 8:   1: [2.6, "m", "# 1"]
11:   2: [3.373, "m", "# 2"]
...
$ asciiframe.sh set 2 marker '# 3'

[2.517264, "o", "t"] [2.6, "m", "# 1"] [2.6, "m", "# 2"] [2.6, "m", "# 3"] [3.372158, "o", "\r\n"]

[2.517264, "o", "t"] [2.6, "m", "# 1"] [2.6, "m", "# 2"] [2.6, "m", "# 3"] [3.372158, "o", "\r\n"]

---

TODO

set_marker () {
    [ ${DEBUG} ] && \
        echo "INFO: set_marker ${1} - ${2}" >> /dev/stderr
    local i
    local x
    local LINE
    local EVENT
    local TEST

    TIME=${1}
   time format unify and strip zero's from end
    TIME=$(float_unify ${TIME})
    (
        ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
/${TIME}, "m/
.=
q
EOF
    ) >> ${TEMP_DIR}/event
    LINE=$(tail -1 ${TEMP_DIR}/event)
    EVENT=$(head -1 ${TEMP_DIR}/event)
    if [ "${EVENT}" == "?" ] ; then
       add new marker
        x=1
        TEST=$(float_to_string ${TIME})
        for i in ${!LINES[@]} ; do
            ((x++))
            [ ${DEBUG} ] && \
                echo "INFO: set_marker() check $x ${LINES[$i]:1} ${TEST}" >> /dev/stderr
            [ ${LINES[$i]:1} -gt ${TEST} ] && \
                LINE="$x" && \
                break
        done
        (
            ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
        ) >> ${TEMP_DIR}/before_event
       jiná událost, jiný soubor
        EVENT=$(head -1 ${TEMP_DIR}/before_event)
        if [ "${EVENT/\"m\"/}" == "${EVENT}" ] ; then
            echo ${EVENT} >> /dev/stderr
            anone "Set a new marker with the description \"${2}\" for time ${TIME} sec? Event will be set before line ${LINE} which was showed"
            [ $? -eq 1 ] && \
                return
           add new event
            ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
i
[${TIME}, "m", "${2}"]
.
w
q
EOF
        else
           následující událost je typu marker
            TIME=$(float_unify ${TIME})
            echo ${EVENT} >> /dev/stderr
            anone "Change the time for this marker from line ${LINE} to ${TIME} sec. and set as description \"${2}\"?"
            [ $? -eq 1 ] && \
                return
           replace event
            ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
d
i
[${TIME}, "m", "${2}"]
.
w
q
EOF
        fi
    else
        echo "${EVENT}" >> /dev/stderr
        anone "Change description of the marker to \"${2}\"?"
        [ $? -eq 1 ] && \
            return
       replace event
    ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
d
i
[${TIME}, "m", "${2}"]
.
w
q
EOF
    fi
    return
}

set_resize () {
    [ ${DEBUG} ] && \
        echo "INFO: set_resize ${1} - ${2}" >> /dev/stderr
    local i
    local x
    local LINE
    local EVENT
    local TEST

    TIME=${1}
   time format unify and strip zero's from end
    TIME=$(float_unify ${TIME})
    (
        ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
/${TIME}, "r/
.=
q
EOF
    ) >> ${TEMP_DIR}/event
    LINE=$(tail -1 ${TEMP_DIR}/event)
    EVENT=$(head -1 ${TEMP_DIR}/event)
    if [ "${EVENT}" == "?" ] ; then
       add new resize event
        x=1
        TEST=$(float_to_string ${TIME})
        for i in ${!LINES[@]} ; do
            ((x++))
            [ ${DEBUG} ] && \
                echo "INFO: set_resize() check $x ${LINES[$i]:1} ${TEST}" >> /dev/stderr
            [ ${LINES[$i]:1} -gt ${TEST} ] && \
                LINE="$x" && \
                break
        done
        (
            ${ED} "${RECORDFILE}" -q -s 2>/dev/null <<-EOF
${LINE}p
q
EOF
        ) >> ${TEMP_DIR}/before_event
       jiná událost, jiný soubor
        EVENT=$(head -1 ${TEMP_DIR}/before_event)
        if [ "${EVENT/\"r\"/}" == "${EVENT}" ] ; then
            echo ${EVENT} >> /dev/stderr
            anone "Set a new resize event \"${2}\" for time ${TIME} sec? Event will be set before line ${LINE} which was showed"
            [ $? -eq 1 ] && \
                return
           add new resize event
            ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
i
[${TIME}, "r", "${2}"]
.
w
q
EOF
        else
           následující událost je typu resize
            TIME=$(float_unify ${TIME})
            echo ${EVENT} >> /dev/stderr
            anone "Change the time for this resize event from line ${LINE} to ${TIME} sec. and change value to \"${2}\"?"
            [ $? -eq 1 ] && \
                return
           replace event
            ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
d
i
[${TIME}, "r", "${2}"]
.
w
q
EOF
        fi
    else
        echo "${EVENT}" >> /dev/stderr
        anone "Change value of the resize event to \"${2}\"?"
        [ $? -eq 1 ] && \
            return
       replace event
    ${ED} "${RECORDFILE}" -s 2>/dev/null <<-EOF
${LINE}
d
i
[${TIME}, "r", "${2}"]
.
w
q
EOF
    fi
    return
}

split_events()

Funkce sloučí události

split_events()
{
    if [ "${1}" -lt "2" ] || [ "${1}" -gt "$((${#LINES[@]}+1))" ] ; then
        echo "ERROR: Line ${1} is out of range. The last line ${RECORDFILE} is $((${#LINES[@]}+1))" >> /dev/stderr
        return 1
    fi
   rozděluje na sérii událostí
    case $(value ${2}) in
        flow) TEMP=${2}
        ;;
        *) TEMP="0.1"
    esac
    IDLE=$(float_to_string ${TEMP})
    SPLIT=($(jq --slurp '.['$((${1}-1))'] | "\(.[0] * 1000000 | floor as $bar | $bar):\(.[2] | explode | .[] as $foo | [$foo] | implode)"' ${RECORDFILE}))
    #echo ${#SPLIT[@]} >> /dev/stderr
    #echo "${SPLIT[@]}" >> /dev/stderr
    TEST=$(get_context ${1})
    echo "TEST ${#TEST[@]} ${TEST[@]}" >> /dev/stderr
    FRAME=${TEST%;*}
    NEXT=${TEST##*;}
    TIME=${FRAME##*:}

    if [ -z "${NEXT/:/}" ] ; then
       poslední řádek, mohu pokračovat
        #
    else
        if [ ${NEXT%:*} -ge ${#LINES[@]} ] ; then
            anone "The interval is too short. You can do interrupt of action and do shift the nexts events first or used decreased interval of writing. Or you want want to go ahead and aplicated the same time for all events?"
            [ $? -eq 1 ] && return
            #
        else
            TEST=$((${NEXT#*:} - ${TIME}))
            SHIFT="$((${#SPLIT[@]} * ${IDLE}))"
            if [ "${SHIFT}" -gt "${TEST}" ] ; then
                anone "The interval is too short. You can do interrupt of action and do shift the nexts events first or used decreased interval of writing. Or you want want to go ahead and aplicated the same time for all events?"
                [ $? -eq 1 ] && return
               Výkonný kód - stejný čas
                #
            else
                #
            fi
        fi
    fi
    return
}

splittext()

Funkce projede asciicast a zobrazované události typu o, pomocí funkce sm() rozděluje na jednoznakové eventy, s tím, že pro každou speciální sekvenci i znak se vytvoří samostatná událost, se stejným časem, pokud není předán parametr $3, kterým se nastavuje čas prodlení mezi znaky.

Tak vzniknou rozsahy událostí, zobrazovaných ve stejný čas, co se dají vyhledat přes funkci get_scope() a po úpravách opět sloučit.

Bez parametrů zpracuje tahle funkce celý obsah souboru, bez toho, že by se dělala nějaké změny v časování.

Parametrem $1 je číslo řádku události LINE, jejímž zpracováním se má začít, a parametrem $2 číslo řádku LINE, události, kterou se má zpracování ukončit.

Parametrem $3 je časový interval, který se má připočítávat k původnímu času rozdělovaných událostí.

TODO - až budou ošetřeny všechny varianty

splittext () {
    [ ${DEBUG} ] && \
        echo "INFO: splittext() ${1} - ${2} - ${3}" >> /dev/stderr
    local line
    local MESS
    local TIME

    x=0
    ${JQ} -c '[.[] ] | .[2] | tostring' ${RECORDFILE} | sed 's/\\/\\\\/g' | while read line ; do
        ((x++))
        [ $x -eq 1 ] && [ -z "${1}" ] && sed -n 1p ${RECORDFILE} && continue
        if [ -z "${1}" ] ; then
           funkce je zavolaná bez parametrů
            INDEX=$(($x-2))
            TYPE="${LINES[${INDEX}]:0:1}"
            case ${TYPE} in
                r|m) sed -n ${x}p ${RECORDFILE} && continue
                ;;
                *) TIME=$(float_from_string ${LINES[${INDEX}]:1})
                    MESS=($(sm "${line}"))
                    for i in ${!MESS[@]} ; do
                        case "${MESS[$i]}" in
                            '"')
                            ;;
                            '\u001b') SPECIAL="${MESS[$i]}"
                            ;;
                            [0-9]|'['|';')\u001b[38;5;
                                [ -z "${SPECIAL}" ] && \
                                echo "[${TIME}, \"o\", \"${MESS[$i]}\"]" || \
                                SPECIAL="${SPECIAL}${MESS[$i]}"
                            ;;
                            'm') [ -z "${SPECIAL}" ] && \
                                echo "[${TIME}, \"o\", \"${MESS[$i]}\"]" || \
                                echo "[${TIME}, \"o\", \"${SPECIAL}${MESS[$i]}\"]" && SPECIAL=''
                            ;;
                            '&asterisk;') echo "[${TIME}, \"o\", \"*\"]"
                            ;;
                            '˙') echo "[${TIME}, \"o\", \".\"]"
                            ;;
                            ' ') echo "[${TIME}, \"o\", \" \"]"
                            ;;
                            *) [ -z "${SPECIAL}" ] && \
                                echo "[${TIME}, \"o\", \"${MESS[$i]}\"]" || \
                                SPECIAL="${SPECIAL}${MESS[$i]}"
                            ;;
                        esac
                    done
                ;;
            esac
        else
            echo ne
           funkce je zavolaná s parametry
            #TEST=$(echo -e "\x5Cu001b\x5D")
            #echo ${line:1:6}
            case ${line:1:2} in
                '\b') ##echo "Začíná znakem back... ${line:0:8}"
                    sm "${line}"
                ;;
                '\r') ##echo "Začíná znakem return... ${line:0:8}"
                    sm "${line}"
                ;;
                '\n') ##echo "Začíná znakem nové řádky... ${line:0:8}"
                    sm "${line}"
                ;;
                '\u')
                   událost začíná speciálním znakem
                    case ${line:1:6} in
                        '\u001b') case ${line:7:1} in
                            ']')echo "Nová řádka... ${line:0:8}"
                                sm "${line}"
                                ;;
                            '[') #echo "Zvýraznění... ${line:0:8}"
                                sm "${line}"
                                ;;
                            *) echo "Něco jiného... ${line:6:7}"
                                ;;
                            esac
                        ;;
                        '\u0007') ##echo "Zalomení textu... ${line:0:8}"
                            sm "${line}"
                        ;;
                    esac
                ;;
                \\?) ##echo "Backslash... ${line:0:8}"
                    sm "${line}"
                ;;
                *)může začínat znakem [ - rozdělené zvýraznění
                    case ${#line} in
                        3)odfiltrování jednoznakových zpráv bez speciálních znaků
                        ;;
                        *) echo "XXX ${LINES[$(($x-2))]:0:1} $x: ${line:1}"
                        ;;
                    esac
                ;;
            esac
            echo "YYY ${LINES[$(($x-3))]:0:1} $x: ${line:1}"
        fi
    done
    return
}

VVV Nepoužívané funkce

addline()

Funkce zapisuje události, generované při akci set

addline () {
    echo "INFO: addline() LINE ${LINE} TYPE ${TYPE} CONTENT ${@}" >> /dev/stderr
    return
TIME="${1}"
TYPE="${1}"
STRING="${1#*\ }"
    ed "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE}p
${LINE}c
${STRING}
.
w
q
EOF
    echo "Line ${LINE} was replaced" >> /dev/stderr
    return
}

changeline()
{

LINE="${1%%\ *}"
STRING="${1#*\ }"
    ed "${RECORDFILE}" 2>/dev/null <<-EOF
${LINE}p
${LINE}c
${STRING}
.
w
q
EOF
    echo "Line ${LINE} was replaced" >> /dev/stderr
    return
}

event_move()
{
                        echo "MARKER přesun značky ORDER ${1} na čas FLOW ${3}" >> /dev/stderr
                        echo "Zjišťuji řádek" >> /dev/stderr
                        LINE=($(item_by_flow ${1} ${3}))
                        echo "PRED ${LINE[@]}"
                        AFTERFRAME=($(frame_by_order ${1} $(( $(frame_order ${1} ${LINE[1]}) - 1 )) ))
                        if [ "${START[0]}" -ge "${AFTERFRAME[0]}" ] ; then
                            echo "Přesouvám značku ${2} na čas ${3}, před rámec ${AFTERFRAME[@]}" >> /dev/stderr
                            echo """
${START[0]}t${AFTERFRAME[0]}
s/${START[1]}/${3}/
$((${START[0]} + 1))d
w
q
""" | ed ${1}
                        else
                            echo "Přesouvám značku ${2} na čas ${3}, před rámec ${AFTERFRAME[@]}" >> /dev/stderr
                            echo """
${START[0]}t${AFTERFRAME[0]}
s/${START[1]}/${3}/
${START[0]}d
w
q
""" | ed ${1}
                        fi
    return
}

make_clip()
{
    [ ${DEBUG} ] && echo """
    Funkce 'make_clip' vygeneruje klip, včetně záhlaví, který lze rovnoou
    předat k přehrání aplikaci asciinema.
    Časy se přepočítají podle parametru \$4 bude-li uveden
    1 - soubor (file)
    2 - od (int)
    3 - do (int)
    4 - čas (flow)
    5 - nepoviný (file)
    """ && \
    item_list && \
    return

    POSUN=${4=0.0}
    #echo ${HEADERSTRING}

    (
    item_list ${1} flow | while read line ; do
        FRAME=(${line})
        if [ ${FRAME[0]} -ge ${2} ] && [ ${FRAME[0]} -le ${3} ] ; then
            echo "${FRAME[0]},s/${FRAME[1]}/$(plustime ${FRAME[1]} ${POSUN})/"
        else
            echo "IGNORE ${FRAME[@]}" >> /dev/stderr
        fi
    done
    [ -z "${5}" ] && echo """$2,$(($3 - 1))p
q
""" || echo """1,$2d
$3,$d
w ${5}
q""" ) | ed -s "${1}"

}

Zpracování předaných parametrů

${FORCE} Potlačí dotazování. U všech operací, které zasahují do záznamů, se bude postupovat jako kdyby byla odpověď ANO.

-h|--help Vyvolá přerušení skriptu a zobrazí dostupnou nápovědu.

-d|--debug Zapne ukecaný mód. Vypisované zprávy jdou na stderr.

Volba zobrazí logo a kec v angličtině, o tom, co vedlo ke vzniku tohoto skriptu

case ${1} in
    -f|--force) FORCE=yes && shift
    ;;
    -h|--help) HELP=yes && shift
    ;;
    -i|--info) INFO=yes && shift
    ;;
    -d|--debug) DEBUG=yes && shift
    ;;
    -v|--version) echo "${VERSION} ${AUTHOR}" >> /dev/stderr && \
        exit
    ;;
    -x) if [ ${2} ] ; then
            EGG=yes && shift
        else
            logo
            exit
        fi
    ;;
esac

_actions()

Funce ověřuje, zda předaný řetězec s názvem akce skutečně odpovídá názvu implementované akce.

$ asciiframe ACTION ascii.cast [clip.cast] COMMAND [PARAM]

Současná verze podporuje tyto akce a příkazy:

clip se používá pro export událostí do klipů a import upravených klipů zpět do asciicastu. Umožňuje také zkrátit, nebo také prodlužit interval zobrazení zobrazované události tím, že upraví časování všech událostí po ní následujících. Má pouze jeden příkaz :

diff , který umožňuje před vložením klipu srovnat události obou souborů a zkontrolovat, zdali už klip náhodou nebyl vložen. Za příkazem, se uvádí jako parametr (PARAM) název aplikace která umí porovnat obsah dvou dočasných souborů: meld, kdiff3, kompare, diffuse'', atp. Pokud parametr chybí, použije se výchozí aplikace –diff'''.

event

- decolor odstraňuje z událostí sekvence co mění vlastnosti použitého fontu
- delete odstraňuje události z asciicastu
- dump odbarvuje výstup na terminál
- merge
- pause posouvá čas události v rámci jejího INTERVALu
- split
- text před vyhledáním textového řetězce slučuje jednoznakové události, které neobsahují speciální skvenci
- time nastavuje událostem stejný čas

find

merge - chybí kotva

play umožňuje přehrávání klipů, protože asciinema má sama o sobě jen omezené možnosti přehrávání: neumožňuje přehrávání od určitého bodu, neumí změnit velikost terminálu podle velikosti uvedené v hlavičce asciicastu a značky interpretuje pouze jako body, kdy přehrávání pozastaví.

set je akce která umožňuje přidávat do asciicastu nové události a měnit jejich obsah, nebo typ.

blink - zatím neimplementováno
breakline
clear
color
comment
duration
enter
root
user
line
marker
newline
reset - zatím neimplementováno
replace - zatím neimplementováno
resize
size
time - ve stadiu rozpracování
title
screen - zatím neimplementováno

split rozdělí zobrazované události typu o předaného asciicastu, na série jednoznakových zobrazovaných událostí. Jaký to má význam, je rozebráno v dokumentaci akce.

view

context
h
info
m
o
r
scope

Pokud předaný řetězec neodpovídá žádné implementované akci, volá se funkce help() která vypíše generickou nápovědu, kde je seznam implementovaných akcí uveden.

split

Při akci split se rozdělí zobrazovaná událost typu o, podle předaných parametrů, na sérii zobrazovaných události, které během přehrávání vyvolají dojem, jako by text na příkazové řádce právě někdo psal. Viz příklad

$ asciiframe.sh view ascii.cast 0.123
4: [0.100000, "o", "echo"]
$ asciiframe.sh view ascii.cast 5
5:   3: [0.356846, "o", "\r\n"]
$ asciiframe.sh split ascii.cast 4 0.001

view

_actions() {
    case ${1} in
        clip|\
        edit|\
        event|\
        find|\
        play|\
        split|\
        merge|\
        'set'|\
        view) if [ ${HELP} ] ; then
                case $(value ${2}) in
                    stri) help ${1}_${2}
                    ;;
                    *) help ${1}
                    ;;
                esac
                exit
            elif [ ${EGG} ] ; then
                case ${1} in
                    event) _manual_set_poster
                    ;;
                    split) _manual_export_script
                    ;;
                esac
                exit
            fi
        ;;
        *) help && exit
        ;;
    esac
}
_actions ${1} ${3}

[ ${#@} -eq 1 ] && help ${1} && exit

Natažení souboru typu asciicast

Formát, který používá aplikace asciinema, tzv. asciicast, je popsán v nápovědě. Jde o soubor, který má data uložená v komprimovaném JSON formátu, který je ovšem možné udržovat i ve strukturované formě, která je výhodnější při nahrazování textových řetězců, např. když chceme konkrétní uživatelské jméno v záznamu nahradit nějakou obecnou formou, atp.

Proto se hned zkraje soubor otestuje a v případě potřeby konverze se vytvoří dočasný soubor, do něhož se pomocí aplikace jq uloží obsah ve standardní komprimované formě.

if [ -f ${2} ] ; then
    header_load ${2}
    [ -z ${RECORDFILE} ] && \
        echo "ERROR: File not asciinema record file or missing!" && \
        exit 1
    cast_load
    [ ${DEBUG} ] && \
        header_print >> /dev/stderr
else
    [ ${HELP} ] && \
        help file || \
            echo "ERROR: File not asciinema record file or missing!" && \
            exit 1
fi

Přidávám další parametry

TEST="$(value ${1})$(value ${2})$(value ${3})$(value ${4})"
case "${TEST}" in
    strifile)
        case ${1} in
            edit)Editace title asciicastu
                splittext
                exit 3
            ;;
            merge) do_merge
            ;;
            play) if [ ${HELP} ] ; then
                    help ${1}
                else
                    asciiplayer
                fi
            ;;
            'set') help "${1}"
            ;;
            split)Rozdělení událostí asciicastu
                 splittext
            ;;
            view) [ ${HELP} ] && help ${1} || get_view 1 ${#LINES[@]}
            ;;
            *)
                [ ${HELP} ] && help ${1} || help
            ;;
        esac
    ;;
    strifilefile)
        case ${1} in
            clip) include_clip $(float_from_string ${LINES[-1]:1}) 0.0 ${3}
            ;;
        esac
    ;;
    strifilefileflow)
        case ${1} in
            clip) case $(value ${5}) in
                    flow|\
                    nflo) include_clip ${4} ${5} ${3}
                    ;;
                    time) include_clip ${4} $(print_seconds ${5}) ${3}
                    ;;
                    *) include_clip ${4} 0.0 ${3}
                    ;;
                esac
            ;;
        esac
    ;;
    strifilefileinte)
        case ${1} in
            clip) case $(value ${5}) in
                    flow|\
                    nflo) include_clip ${4} ${5} ${3}
                    ;;
                    time) include_clip ${4} $(print_seconds ${5}) ${3}
                    ;;
                    *) include_clip ${4} 0.0 ${3}
                    ;;
                esac
            ;;
        esac
    ;;
    strifilefilestri)
        case ${1} in
            clip) case "${4}" in
                    diff) DIFF=$(command -v ${5})
                        if [ -x "${DIFF}" ] ; then
                            ${DIFF} <(
                            ${JQ} '[.[] ] | ((.[1] | tostring) + " – " + (.[2] | tostring))' ${2} |\
                            ${SED} 's/\ –\ /\", \"/') <(
                            ${JQ} '[.[] ] | ((.[1] | tostring) + " – " + (.[2] | tostring))' ${3} |\
                            ${SED} 's/\ –\ /\", \"/')
                        else
                            diff -y <(
                            ${JQ} '[.[] ] | ((.[1] | tostring) + " – " + (.[2] | tostring))' ${2} |\
                            ${SED} 's/\ –\ /\", \"/') <(
                            ${JQ} '[.[] ] | ((.[1] | tostring) + " – " + (.[2] | tostring))' ${3} |\
                            ${SED} 's/\ –\ /\", \"/')
                        fi
                    ;;
                    *) help clip_diff
                esac
            ;;
            event) do_event ${4} ${3}
           dump?
            ;;
        esac
    ;;
    strifilefiletime)
        case ${1} in
            clip) TIME=$(print_seconds ${4})
                if [ -z "${5}" ] ; then
                    echo "Vkládám klip ${3} na čas ${TIME}, s nulovým posunem časování" >> /dev/stderr
                    include_clip ${TIME} 0.0 ${3}
                else
                    case $(value ${5}) in
                        flow|nflo)
                            echo "Vkládám klip ${3} na čas ${TIME}, s posunem časování ${5}" >> /dev/stderr
                            include_clip ${TIME} ${5} ${3}
                        ;;
                        time) PAUZA= $(print_seconds ${5})
                            echo "Vkládám klip ${3} na čas ${TIME}, a za ním bude PAUZA ${PAUZA}" >> /dev/stderr
                            include_clip ${TIME} ${PAUZA} ${3}
                        ;;
                    esac
                fi
            ;;
        esac
    ;;
    strifileflow)
        case ${1} in
            event) help {1}
            ;;
            clip)echo "Print clip from line identified by float time $3
                START=$(float_to_string ${3})
                for i in ${!LINES[@]} ; do
                    [ ${i} -eq 0 ] && continue
                    if [ "${LINES[$i]:1}" -ge "${START}" ] ; then
                        clip_print $(($i+1)) ${#LINES[@]}
                        break
                    fi
                done
            ;;
            view) if [ "${3//:/}" == "${3}" ] ; then
                    get_view ${3}
                else
                    get_view $(print_seconds ${3}) ${4}
                fi
            ;;
            play) clip_print ${3}
           | xterm ...
                echo TODO
            ;;
        esac
    ;;
    strifileflowflow)
        case ${1} in
            clip)Print clip from line identified by float time $3
                 into line identified by float time $4
                START=$(float_to_string ${3})
                for i in ${!LINES[@]} ; do
                    [ ${i} -eq 0 ] && continue
                    if [ "${LINES[$i]:1}" -ge "${START}" ] ; then
                        END=$(float_to_string ${4})
                        for y in ${!LINES[@]} ; do
                            [ ${y} -eq 0 ] && continue
                            if [ "${LINES[$y]:1}" -ge "${END}" ] ; then
                                clip_print $(($i+1)) $(($y+1))
                                break
                            fi
                        done
                        break
                    fi
                done
            ;;
            view) if [ "${3//:/}" == "${3}" ] ; then
                    get_view ${3} ${4}
                else
                    get_view $(print_seconds ${3}) ${4}
                fi
            ;;
        esac
    ;;
    strifileflowinte)
        case ${1} in
            clip)echo "Print clip from line identified by float
                 time $3 into line $4
                START=$(float_to_string ${3})
                for i in ${!LINES[@]} ; do
                    [ ${i} -eq 0 ] && continue
                    if [ "${LINES[$i]:1}" -ge "${START}" ] ; then
                        clip_print $(($i+1)) ${4}
                        break
                    fi
                done
            ;;
            view) if [ "${3//:/}" == "${3}" ] ; then
                    get_view ${3} ${4}
                else
                    get_view $(print_seconds ${3}) ${4}
                fi
            ;;
            play) if [ ${HELP} ] ; then
                    help ${1}
                else
                    START=$(float_to_string ${3})
                    for i in ${!LINES[@]} ; do
                        [ ${i} -eq 0 ] && continue
                        if [ "${LINES[$i]:1}" -ge "${START}" ] ; then
                            clip_print $(($i+1)) ${4} | asciiplayer
                            break
                        fi
                    done
                fi
            ;;
        esac
    ;;
    strifileflownflo)
        case ${1} in
            view) if [ "${3//:/}" == "${3}" ] ; then
                    get_view ${3} ${4}
                else
                    get_view $(print_seconds ${3}) ${4}
                fi
            ;;
            play) [ ${HELP} ] && help ${1} || \
                clip_print ${3} {4} | asciiplayer
            ;;
        esac
    ;;
    strifileflownint)
        case ${1} in
            view) if [ "${3//:/}" == "${3}" ] ; then
                    get_view ${3} ${4}
                else
                    get_view $(print_seconds ${3}) ${4}
                fi
            ;;
            play) [ ${HELP} ] && help ${1} || \
                clip_print ${3} {4} | asciiplayer
            ;;
        esac
    ;;
    strifileflowrang)
        echo TODO >> /dev/stderr
    ;;
    strifileflowstri)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            *)Ostatní akce nemají obvykle za identifikátorem řetězec
                help ${1}
            ;;
        esac
    ;;
    strifilehelp)
        case ${1} in
            event|\
            find|\
            clip|\
           'set') help ${1}
            ;;
            play)parametr --speed=
                [ ${HELP} ] && help ${1} || \
                    PLAY="${3}" asciiplayer
            ;;
        esac
    ;;
    strifilehelpflow)
        case ${1} in
            clip) case $(value ${5}) in
                    flow|\
                    nflo) include_clip ${4} ${5} --
                        ;;
                    time) include_clip ${4} $(print_seconds ${5}) --
                        ;;
                    *) help clip_move
                        ;;
                esac
            ;;
            play) [ ${HELP} ] && help ${1} || \
                PLAY="${@:3}" asciiplayer
            ;;
        esac
    ;;
    strifilehelpinte)
        case ${1} in
            clip) case $(value ${5}) in
                    flow|\
                    nflo) include_clip ${4} ${5} --
                        ;;
                    time) include_clip ${4} $(print_seconds ${5}) --
                        ;;
                    *) help clip_move
                        ;;
                esac
            ;;
            play) [ ${HELP} ] && help ${1} || \
                PLAY="${@:3}" asciiplayer
            ;;
        esac
    ;;
    strifileinte)
        case ${1} in
            event) help ${1}
            ;;
            clip) clip_print ${3} ${#LINES[@]}
            ;;
            view) get_view ${3}
            ;;
            play) [ ${HELP} ] && help ${1} || \
                clip_print ${3} ${#LINES[@]} 2>/dev/null | asciiplayer
            ;;
        esac
    ;;
    strifileintefile)
       Žádná akce nemá za identifikátorem řetězec
        help ${1}
    ;;
    strifileinteflow)
        case ${1} in
            clip) echo "Posouvám časování od řádky ${3} o ${4} sekund kupředu" >> /dev/stderr
                clip_print ${3} ${4}
            ;;
            view) get_view ${3} ${4}
            ;;
            play) [ ${HELP} ] && help ${1} || \
                clip_print ${3} ${4} 2>/dev/null | asciiplayer
            ;;
        esac
    ;;
    strifileintehelp)
        case ${1} in
            play) [ ${HELP} ] && help ${1} || \
                clip_print ${3} ${#LINES[@]} 2>/dev/null | PLAY="${@:4}" asciiplayer
            ;;
        esac
    ;;
    strifileinteinte)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            play)přehraje se pouze vybraný úsek výchozího souboru
                 [ ${HELP} ] && help ${1} || \
                    playcast ${3} ${4} | asciiplayer
            ;;
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifileintenint)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifileintenflo)
        case ${1} in
            clip) echo "Zkracuji časování od řádky ${3} o ${4} sekund" >> /dev/stderr
                clip_print ${3} ${4}
            ;;
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifileinterang)
        echo TODO >> /dev/stderr
    ;;
    strifileintestri)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            *)Ostatní akce nemají obvykle za identifikátorem řetězec
                help ${1}
            ;;
        esac
        ;;
    strifilenflo)
        case ${1} in
            event) help ${1}
            ;;
            view) get_view ${3}
            ;;
        esac
    ;;
    strifilenfloflow)
        echo TODO >> /dev/stderr
    ;;
    strifilenflointe)
        case ${1} in
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifilenflonflo)
        case ${1} in
            *) help ${1}
            ;;
        esac
    ;;
    strifilenflonint)
        case ${1} in
            *) help ${1}
            ;;
        esac
    ;;
    strifilenflostri)
       Žádná akce nemá za identifikátorem řetězec
        help ${1}
    ;;
    strifilenint)
        case ${1} in
            event|\
            clip) help {1}
            ;;
            view) get_view ${3}
            ;;
        esac
    ;;
    strifilenintflow)
        case ${1} in
            view) get_view ${3} ${4}
            ;;
        esac
        ;;
    strifilenintinte)
        case ${1} in
            clip) help clip
            ;;
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifilenintnflo)
        case ${1} in
            view) get_view ${3} ${4}
            ;;
        esac
        ;;
    strifilenintnint)
        case ${1} in
            clip) help ${1}
            ;;
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifilenintstri)
       Žádná akce nemá za identifikátorem řetězec
        help ${1}
        ;;
    strifilerang|\
    strifilerangstri)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            *)Ostatní akce nemají obvykle za identifikátorem řetězec
                help ${1}
            ;;
        esac
    ;;
    strifilestri)
        case ${1} in
            event) case ${3} in
                    decolor|\
                    delete|\
                    dump|\
                    text) do_event ${3}
                    ;;
                    merge|\
                    split|\
                    time) help ${1}
                    ;;
                esac
            ;;
            find) findline "${3}"
            ;;
            merge) case ${3} in
                    range)Spojení událostí asciicastu, které odpovídají typu rang
                        echo TODO
                    ;;
                esac
            ;;
            'set') case ${3} in
                    duration) do_set ${3}
                    ;;
                    clear|\
                    comment|\
                    duration|\
                    enter|\
                    root|\
                    user|\
                    newline|\
                    marker|\
                    event|\
                    resize) help "set_${3}"
                    ;;
                    *) help "${1}"
                    ;;
                esac
            ;;
            view) [ ${HELP} ] && help ${1}_${3}
                case ${3} in
                    context|info|scope|h|m|o|r) get_view ${3}
                    ;;
                    struct) jq . -C ${2}
                    ;;
                    compact) jq . -c -C ${2} | less -r
                    ;;
                    time) VIEW="time" && get_view 1 ${#LINES[@]}
                    ;;
                    *) help view
                    ;;
                esac
            ;;
        esac
    ;;
    strifilestriflow)
        case ${1} in
            event) case ${3} in
                    delete|\
                    merge|\
                    split|\
                    text|\
                    time) shift && shift && do_event "${@}"
                    ;;
                    decolor) help ${1}_${3}
                    ;;
                esac
            ;;
            find)sem spadnou parametry typu 1;1
                findline "${3}" "${4}"
            ;;
            'set') shift && shift && do_set "${@}"
            ;;
            view) case ${3} in
                    context|info|scope|h|m|o|r) get_view ${3} ${4}
                    ;;
                    *) help view
                    ;;
                esac
            ;;
        esac
    ;;
    strifilestristri)
        case ${1} in
            event) case ${3} in
                    delete|\
                    merge|\
                    split|\
                    text|\
                    time) shift && shift && do_event "${@}"
                    ;;
                    decolor) help ${1}_${3}
                    ;;
                esac
            ;;
            find) shift && shift && do_event "${@}"
            ;;
            'set') echo ${ORIGTITLE} >> /dev/stderr
                case ${3} in
                    size|\
                    resize|\
                    marker|\
                    title) shift && shift && do_set "${@}"
                    ;;
                    *) help ${1}_${3}
                    ;;
                esac
            ;;
            view) help ${1}
            ;;
        esac
    ;;
    strifilestriinte)
        case ${1} in
            event) case ${3} in
                    decolor|\
                    delete|\
                    merge|\
                    pause|\
                    split|\
                    text|\
                    time) shift && shift && do_event "${@}"
                    ;;
                esac
            ;;
            find) findline "${3}" "${4}"
            ;;
            'set') shift && shift && do_set "${@}"
            ;;
            view) case ${3} in
                    context|info|scope|h|m|o|r) get_view ${3} ${4}
                    ;;
                    *) help view
                    ;;
                esac
            ;;
        esac
    ;;
    strifilestrinint)
        case ${1} in
            event) case ${3} in
                    delete|\
                    merge|\
                    split|\
                    text|\
                    time) shift && shift && do_event "${@}"
                    ;;
                    decolor) help ${1}_${3}
                    ;;
                esac
            ;;
            view) help ${1}
            ;;
        esac
    ;;
    strifilestritime)
        case ${1} in
            event) case ${3} in
                    delete|\
                    marker|\
                    resize|\
                    split|\
                    text|\
                    time) shift && shift && do_event "${@}"
                    ;;
                    decolor) help ${1}_${3}
                    ;;
                esac
            ;;
            find)sem spadne omylem zadaný parametr typu 1:1
                help find_colon
            ;;
            view) case ${3} in
                    context|info|scope|h|m|o|r) get_view ${3} ${4}
                    ;;
                    *) help view
                    ;;
                esac
            ;;
            'set') shift && shift && do_set "${@}"
            ;;
        esac
    ;;
    strifilestrirang)
        case ${1} in
            event) case ${3} in
                    merge|\
                    delete) shift && shift && do_event "${@}"
                    ;;
                    decolor) help ${1}_${3}
                    ;;
                esac
            ;;
            view) case ${3} in
                    context|info|scope|h|m|o|r) get_view ${3} ${4}
                    ;;
                    *) help view
                    ;;
                esac
            ;;
            'set') case ${3} in
                    resize|\
                    marker) help ${1}_${3}
                    ;;
                esac
            ;;
        esac
    ;;
    strifiletime)
        case ${1} in
            clip)echo "Print clip from line identified by float time $3
                START=$(print_seconds ${3})
                clip_print ${START}
              START=$(float_to_string ${3})
              for i in ${!LINES[@]} ; do
                  [ ${i} -eq 0 ] && continue
                  if [ "${LINES[$i]:1}" -ge "${START}" ] ; then
                      clip_print $(($i+1)) ${#LINES[@]}
                      break
                  fi
              done
            ;;
            play) clip_print ${3} play
            ;;
            view) get_view $(print_seconds ${3})
            ;;
        esac
    ;;
    strifiletimeflow)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            view) get_view $(print_seconds ${3}) ${4}
            ;;
        esac
    ;;
    strifiletimeinte)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            view) get_view ${3} ${4}
            ;;
        esac
    ;;
    strifiletimenflo)
        case ${1} in
            clip) [ ${DEBUG} ] && \
                echo "Zkracuji časování od „human-ready” času ${3} o ${4} sekund" >> /dev/stderr
                clip_print ${3} ${4}
            ;;
            view) get_view $(print_seconds ${3}) ${4}
            ;;
        esac
    ;;
    strifiletimenint)
        case ${1} in
            clip) clip_print $(print_seconds ${3}) ${4}
            ;;
            view) get_view $(print_seconds ${3}) ${4}
            ;;
        esac
    ;;
    strifiletimestri)
        case ${1} in
            clip) clip_print ${3} ${4}
            ;;
            *)Ostatní akce nemají obvykle za identifikátorem řetězec
                help ${1}
            ;;
        esac
    ;;
    *) echo "Nepodchycená kombinace parametrů ${TEST}" >> /dev/stderr
        help ${1}
    ;;
esac

Kontrolní výpis použitých parametrů

echo "${TEST}" >> /dev/stderr

The End