Come for the code, stay for the community

Asciinema Player

Create a long text formatter to display a asciinema player.

Asciinema is a tool for recording a terminal.

It's easy to use and let's you share the recording on the site.

Implementation

Generate a module with drush gen module.

Theme

In asciinema_player.module, set up a theme hook.

/**
 * Implements hook_theme().
 */
function asciinema_player_theme($existing, $type, $theme, $path) {
  return [
    'asciinema_player' => [
      'render element' => 'player'
    ]
  ];
}

Preprocess

Attach the asciinema_player/init library, and pass the base64 encoded string to drupalSettings.asciinema_player.src

/**
 * Implements hook_preprocess_HOOK().
 */
function asciinema_player_preprocess_asciinema_player(&$variables) {
  $variables['player']['#attached']['library'][] = 'asciinema_player/init';
  $variables['player']['#attached']['drupalSettings']['asciinema_player']['src'] = $variables['player']['#src'];
}

Field Formatter

Generate a field formatter using drush gen plugin:field:formatter.

Edit the AsciinemaPlayerFormatter class.

<?php

declare(strict_types=1);

namespace Drupal\asciinema_player\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Plugin implementation of the 'basic_string' formatter.
 */
#[FieldFormatter(
  id: 'asciinema_player',
  label: new TranslatableMarkup('Asciinema Player'),
  field_types: [
    'string_long',
  ],
)]
class AsciinemaPlayerFormatter extends FormatterBase {

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $elements = [];

    foreach ($items as $delta => $item) {
      $elements[$delta] = [
        '#theme' => 'asciinema_player',
        '#src' => base64_encode($item->value),
      ];
    }

    return $elements;
  }
}

Libraries

Download the .css and .min.js files from the latest Asciinema Player release to asciinema_player/libraries.
https://github.com/asciinema/asciinema-player/releases

Create asciinema_player.libraries.yml.

lib:
  css:
    component:
      libraries/asciinema-player.css: {}
  js:
    libraries/asciinema-player.min.js: {}
    
init:
  js:
    libraries/asciinema_player.js: {}
  dependencies:
    - asciinema_player/lib
    - core/once

lib is the minified player js, and init is this module's script to launch the player.

Create libraries/asciinema_player.js.

((drupalSettings, once, AsciinemaPlayer) => {
  Drupal.behaviors.asciinemaPlayer = {
    attach(context) {
      const players = once('asciinema-player', '.asciinema-player', context);
      players.forEach(player => {
        AsciinemaPlayer.create('data:text/plain;base64,' + drupalSettings.asciinema_player.src, player, {
          idleTimeLimit: 2
        });
      })
      console.log(document.querySelector('.asciinema-player'))
    }
  }
})(drupalSettings, once, AsciinemaPlayer)

See: https://docs.asciinema.org/manual/player/quick-start/

Notice we are passing the cast content as the base64 encoded string stored in drupalSettings.asciinema_player.src.

Media Type

After setting this up, I found a module that converts a url into an embed.

https://www.drupal.org/project/media_remote

You have to create custom plugins. I added one for Asciinema.

https://git.drupalcode.org/project/media_remote

Considerations

The player can use the asciinema cast content as a string, or url to the code.

A file upload field may be better for performance, since the file could be fetched separately.

Maybe it could me a new Media type with an edit field that updates the file.

Drupal Asciinema Player module

Tasks