Serving HLS Content from CUE Front

The tomorrow-online demo publication supplied with CUE Front handles video content in general, but does not support HLS streaming. You can modify it to support HLS streaming using the VideoJS video player as follows:

  1. Make sure the videoJS library is loaded by adding the following lines to templates/_patterns/01-globals/05-nonvisual/_scriptsBlock.twig:

    <script src="https://vjs.zencdn.net/7.8.4/video.js"></script>
    <script src="https://unpkg.com/@videojs/http-streaming@2.1.0/dist/videojs-http-streaming.min.js"></script>
  2. Include the VideoJS stylesheet in templates/_patterns/01-globals/05-nonvisual/_stylesheet.twig:

    <link href="https://vjs.zencdn.net/7.8.4/video-js.css" rel="stylesheet" />
  3. Make the following highlighted changes to templates/_patterns/02-atoms/03-videos/04-cloud-video.twig:

    {% set video = article.mediaInfo.videos[1] %} {# Change the index value to the appropriate video source #}
    {% set captions = article.caption_href %}
    <div class="video-teaser">
      {% if article.mediaInfo.headers is not empty %}
        {% for header in article.mediaInfo.headers if header.key == "Set-Cookie" %}
          {{ set_header("Set-Cookie", header.value) }}
        {% endfor %}
      {% endif %}
      <video id="video-js-{{ article.id }}"
             class="video-js"
             width="{{ video.width }}"
             height="{{ video.height }}"
             controls>
        {#    <source src="{{ video.uri }}" type="{{ video.mimeType }}">#}
        {% if captions is not empty %}
          <track kind="subtitles" src="{{ captions | relativize }}" default>
        {% endif %}
        Your browser does not support HTML5 video.
      </video>
      <script type="text/javascript">
        document.addEventListener("DOMContentLoaded", () => {
          const player = videojs("video-js-{{ article.id }}");
          player.pause();
          player.fluid(true);
          player.src({
            src: '{{ video.uri }}',
            type: '{{ video.mimeType }}',
            withCredentials: true,
          });
          player.load();
        });
      </script>
    </div>
  4. To be able to render inline HLS video in storylines, you also need to make the following highlighted additions to templates/_patterns/02-atoms/11-storyline-elements/storyline-element-video.twig:

    {% set video = element.relation.mediaInfo.videos[1] %}
    {% set autoplay = 0 %}
    {% set allowFullScreen = 0 %}
    {% set closedcaption = 0 %}
     
    {% for fieldValue in element.fields %}
      {% if fieldValue.name == 'closedcaption' and fieldValue.booleanValue == 1 %}
        {% set closecaption = 1 %}
      {% endif %}
      {% if fieldValue.name == 'autoplay' and fieldValue.booleanValue == 1 %}
        {% set autoplay = 1 %}
      {% endif %}
      {% if fieldValue.name == 'allowfullscreen' and fieldValue.booleanValue == 1 %}
        {% set allowFullScreen = 1 %}
      {% endif %}
    {% endfor %}
     
    {% if element.relation.mediaInfo.headers is not empty %}
      {% for header in element.relation.mediaInfo.headers if header.key == "Set-Cookie" %}
        {{ set_header("Set-Cookie", header.value) }}
      {% endfor %}
    {% endif %}
     
    <video id="video-js-{{ element.relation.id }}"
           class="video-js mb2"
           width="{{ video.width }}"
           height="{{ video.height }}"
           controls>
      {#  <source src="{{element.relation.mediaInfo.videos[1].uri}}" >#}
      <track label="English"
             srclang="en"
             src="{{ element.relation.caption_href }}"
          {% if closecaption == 1 %}
            kind="subtitles"
          {% endif %}
             default
      >
      Your browser does not support HTML5 video.
    </video>
     
    {% if  element.relation.fields.description is not empty %}
      <div class="f6 header-font mt3 primary mb4">
        {{ element.relation.fields.description }}
      </div>
    {% endif %}
     
    <script type="text/javascript">
      document.addEventListener("DOMContentLoaded", () => {
        const player = videojs("video-js-{{ element.relation.id }}", {
          autoplay: {% if autoplay == 1 %}true{% else %}false{% endif %},
          BigPlayButton: true,
          fluid: true,
          children: {
            controlBar: {
              subtitlesButton: true,
              captionsButton: true,
              fullscreenToggle: {% if allowFullScreen == 1 %} true {% else %} false {% endif %}
            }
          }
        });
     
        player.pause();
        player.src({
          src: '{{ video.uri }}',
          type: '{{ video.mimeType }}',
          withCredentials: true,
        });
        player.load();
     
        player.ready(() => {
          {% if allowFullScreen == 0 %}
          // Disable entering full screen on double click
          player.tech_.off('dblclick');
          {% endif %}
        });
      });
    </script>
  5. If you are using signed cookies for access control, then you will also need to add the following GraphQL code to recipe/queries/video.graphql:

    ... on Video {
        mediaInfo {
            headers {
                key
                value
            }
        }
    }

    This makes sure that the requires headers are included in the JSON data returned by the Cook. The template changes listed above for 04-cloud-video.twig and 03-videos/04-cloud-video.twig include code for forwarding any supplied headers:

      {% if article.mediaInfo.headers is not empty %}
        {% for header in article.mediaInfo.headers if header.key == "Set-Cookie" %}
          {{ set_header("Set-Cookie", header.value) }}
        {% endfor %}
      {% endif %}

    If you are not intending to use signed cookies, then you can omit this code from both templates.