Custom playlist reader implementation

Advanced users can create their own implementation of the playlist reader. This allows you to create the entire player interface yourself and hide the default player. The player's functions are controlled through JavaScript using the API provided by the reader.

Preparation

Enable the playlist reader normally according to the instructions on the previous page.

When the playlist reader is enabled, it automatically creates the window.AimPlaylist namespace through which the reader can be controlled.

Playlist reader availability

The playlist reader's namespace is only available after the reader's script has been loaded. You can check availability directly through the namespace or by listening for the 'AimPlaylist.ready' event.

if (window.AimPlaylist?.ready) {
    // The playlist reader is available
} else {
    window.addEventListener('AimPlaylist.ready', () => {
        // The playlist reader is available
    }, { once: true });
}

Hiding the default player

When creating your own playlist player interface, it's recommended to hide the default player completely. You can hide the player using the following CSS code.

.aim-playlist-host {
    display: none !important;
}

Multiple playlists

If you have multiple playlists visible on your website, you can identify the playlists by giving them a unique ID. The ID is defined with the data-aim-playlist-id attribute.

<div class="aim-playlist" data-aim-playlist-id="my-playlist-1">
    ...
</div>

<div class="aim-playlist" data-aim-playlist-id="my-playlist-2">
    ...
</div>

After this, you can use the ID in the API methods that support it. For example, you can start playing the second playlist by calling window.AimPlaylist.play('my-playlist-2').

API

When the AimPlaylist namespace is available, the following properties and functions can be used via the namespace, for example window.AimPlaylist.play().

Properties

Property Type Description
ready true, readonly Whether the AimPlaylist namespace is ready for use.
speed number The reader's playback speed between 0.5 - 4.0. Defaults to 1.0. You can change the speed by assigning a new value to this property.
volume number The reader's volume between 0.0 - 1.0. Defaults to 1.0. You can change the volume by assigning a new value to this property.

Methods

Method Return value Description
next(playlistId?: string) Promise<void> Move to the next sentence. The Promise resolves when the next sentence's playback has started.
nextPlaylist() Promise<void> Move to the next playlist. The Promise resolves when the next playlist playback has started.
nextPlaylistItem(playlistId?: string) Promise<void> Move to the next link in the playlist. The Promise resolves when the next link's playback has started.
pause() void Pause the playback. The playback can be resumed by calling play() or playPause().
play(playlistId?: string) Promise<void> Start playback or resume paused playback. The Promise resolves when the playback has started.
playPause(playlistId?: string) Promise<void> Toggle between pause and play states. The Promise resolves when the playback has started.
previous(playlistId?: string) Promise<void> Move to the previous sentence. The Promise resolves when the previous sentence's playback has started.
previousPlaylist() Promise<void> Move to the previous playlist. The Promise resolves when the previous playlist playback has started.
previousPlaylistItem(playlistId?: string) Promise<void> Move to the previous link in the playlist. The Promise resolves when the previous link's playback has started.
stop() void Stop the playback completely and remove the highlighting of the links. The playback cannot be resumed from the same point, only started from the beginning.

Events

The playlist reader sends some events, which you can listen to using the window.addEventListener function. The event name is of the form 'AimPlaylist.<event>', for example 'AimPlaylist.loading'. If the event has data, it can be found under the detail key. The data also includes playlistId, which you can use to identify which playlist the event belongs to.

Event name Data Description
'AimPlaylist.duration' { durationRemaining: number, playlistId: string, totalDuration: number } The estimated total duration of the current link and the remaining duration in seconds. This event is sent every second or whenever a duration changes. The playback speed affects the audio duration.
'AimPlaylist.error' { error: string | false, playlistId: string } An error occurred in the reader. Possible error values:
- 'link-parsing-failed' - The link's text content could not be parsed.
- 'server-error' - The server does not respond normally.
- 'no-text-content-found' - No text content found in the .aim-readable elements.
- 'unknown-error' - Unknown error.
- false - Previous error was cleared.
'AimPlaylist.loading' { loading: boolean, playlistId: string } The reader starts or stops loading.
'AimPlaylist.playback' { action: 'play' | 'pause' | 'stop', playlistId: string, playlistItemIndex: number } The playback state changed. action tells what happened. playlistItemIndex tells which link the event belongs to.
'AimPlaylist.ready' void The reader's namespace window.AimPlaylist is ready for use.

Example

<html>
    <head>
        <!-- Load the playlist reader script -->
        <script src="https://portal.aimater.com/static/reader/playlist/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.js"></script>

        <script>
            /**
             * Enable the custom reader when the `AimPlaylist` namespace is ready.
             */
            function onAimPlaylistReady() {
                // Make your custom reader visible.
                const myCustomReader = document.getElementById('my-custom-playlist-reader');
                myCustomReader?.removeAttribute('hidden');

                // Show a loading icon when the reader is loading.
                const loadingIcon = document.getElementById('loading-icon');
                window.addEventListener(
                    'AimPlaylist.loading',
                    (event) => {
                        if (event.detail.loading) {
                            loadingIcon?.removeAttribute('hidden');
                        } else {
                            loadingIcon?.setAttribute('hidden', '');
                        }
                    },
                );

                // Show an error message if the reader encounters an error.
                const errorMessage = document.getElementById('error-message');
                window.addEventListener(
                    'AimPlaylist.error',
                    (event) => {
                        if (event.detail.error) {
                            errorMessage?.removeAttribute('hidden');
                        } else {
                            errorMessage?.setAttribute('hidden', '');
                        }
                    },
                );

                // Show the remaining duration of the current link.
                const durationElement = document.getElementById('duration');
                window.addEventListener(
                    'AimPlaylist.duration',
                    (event) => {
                        durationElement.innerHTML = `Duration: ${event.detail.durationRemaining} s`;
                    },
                );

                // Set initial values for volume and speed inputs.
                const volumeInput = document.getElementById('volume-input');
                const speedInput = document.getElementById('speed-input');
                if (volumeInput) {
                    volumeInput.value = window.AimPlaylist.volume;
                }
                if (speedInput) {
                    speedInput.value = window.AimPlaylist.speed;
                }
            }

            // Wait for the reader to be ready.
            if (window.AimPlaylist?.ready) {
                onAimPlaylistReady();
            } else {
                window.addEventListener(
                    'AimPlaylist.ready',
                    onAimPlaylistReady,
                    { once: true },
                );
            }
        </script>

        <style>
            /* Hide the default player. */
            .aim-playlist-host {
                display: none !important;
            }
        </style>
    </head>

    <body>
        <aside id="my-custom-playlist-reader" hidden>
            <div id="loading-icon" hidden></div>

            <div id="error-message" hidden>An unexpected error occurred.</div>

            <button type="button" onclick="window.AimPlaylist.play()">Play</button>
            <button type="button" onclick="window.AimPlaylist.pause()">Pause</button>
            <button type="button" onclick="window.AimPlaylist.stop()">Stop</button>
            <button type="button" onclick="window.AimPlaylist.previousPlaylistItem()">Previous article</button>
            <button type="button" onclick="window.AimPlaylist.nextPlaylistItem()">Next article</button>

            <label for="volume-input">Volume</label>
            <input
                id="volume-input" type="range"
                min="0" max="1" step="0.1"
                oninput="window.AimPlaylist.volume = this.value"
            />
            <label for="speed-input">Speed</label>
            <input
                id="speed-input" type="range"
                min="0.5" max="4" step="0.1"
                oninput="window.AimPlaylist.speed = this.value"
            />

            <div id="duration">Duration: 0 s</div>
        </aside>

        <div class="aim-playlist">
            <a href="article-1">...</a>
            <a href="article-2">...</a>
            <a href="article-3">...</a>
            <a href="article-4">...</a>
        </div>
    </body>
</html>