bbc / peaks.js

JavaScript UI component for interacting with audio waveforms
https://waveform.prototyping.bbc.co.uk
GNU Lesser General Public License v3.0
3.2k stars 279 forks source link

server side rendering and peaks.js #335

Closed jamesb93 closed 4 years ago

jamesb93 commented 4 years ago

Using gatsby.js I am attempting to make peaks.js a component as part of a documentation website.

When building my gatsby site using gatsby develop I get this error:

  13053 | module.exports = function (Cue, Utils) {
  13054 |     'use strict';
> 13055 |     var isHeadless = /HeadlessChrome/.test(navigator.userAgent);
        | ^
  13056 |     function windowIsVisible() {
  13057 |         if (isHeadless || navigator.webdriver) {
  13058 |             return false;

With some preliminary research the issue is to do with the fact that at the compilation stage there is no DOM and so no navigator (or something to that effect - excuse my ignorance for all things web dev).

Similar issues can be found here:

https://github.com/gatsbyjs/gatsby/issues/19317 https://github.com/gatsbyjs/gatsby/issues/7290

chrisn commented 4 years ago

Peaks.is is a front-end component, so accessing navigator is perfectly reasonable. I don't know Gatsby, so can't advise on how to fix the issue.

jamesb93 commented 4 years ago

Of course, I was just deferring as I'm not sure this is something that can be worked around reasonably and without massively forking peaks.js in order to get it functional.

No worries.

jamesb93 commented 4 years ago

I wasn't able to get this working as I wanted to in gatsby.js and as a React component, however, I was able to overcome this in the Sapper/Svelte framework.

Here is the component

<script>
    import { onMount } from "svelte";
    let Peaks;
    let instance;
    let overview;
    let zoom;
    let audio;
    export let title;
    export let file;
    export let buffer;

    onMount (async () => {

        // SSR Compatability
        const module = await import("peaks.js");
        Peaks = module.default;

        // Generate an instance form factory
        instance = Peaks.init({
            containers: {
                zoomview: zoom,
                overview: overview
            },
            dataUri: {
                arraybuffer: buffer
            },
            mediaElement: audio,
            height: 60,
            segmentStartMarkerColor: '#a0a0a0',
            segmentEndMarkerColor: '#a0a0a0',
            zoomWaveformColor: 'rgba(0, 30, 128, 0.61)',
            overviewWaveformColor: 'rgba(0, 15, 100, 0.3)',
            overviewHighlightColor: 'grey',
            segmentColor: 'rgba(255, 161, 39, 1)',
            playheadColor: 'rgba(0, 0, 0, 1)',
            playheadTextColor: '#aaa',
            showPlayheadTime: false,
            pointMarkerColor: '#FF0000',
            axisGridlineColor: '#ccc',
            axisLabelColor: '#aaa',
            randomizeSegmentColor: true
        })
    });
</script>

<!-- <svelte:component this={Peaks} /> -->
<h1>{title}</h1>
<div>
    <div bind:this={overview} /> <!-- overview -->
    <div bind:this={zoom} /> <!-- zoom -->
    <div id="peaks-control">
        <audio bind:this={audio}> <!-- audio -->
            <source src={file} type="audio/mp3">
        </audio>
    </div>
</div>

and a useful article:

https://sapper.svelte.dev/docs/#Server-side_rendering

chrisn commented 4 years ago

Good to hear you've got this working, and sorry I couldn't help with Gatsby.js.