Skip to content

Latest commit

 

History

History
725 lines (595 loc) · 19.1 KB

File metadata and controls

725 lines (595 loc) · 19.1 KB

Player Expert integration guide (Amalia)

This document describes how the Player Expert application integrates Amalia (@ina/amalia) in production.

It is intended as a practical reference for:

  • copying the Amalia bundle into a host application
  • loading Amalia at runtime
  • creating the player and plugins as Web Components
  • configuring metadata sources (dataSources) and plugin routing
  • understanding event names used by Player Expert

This guide is based on real code from Player Expert:

  • src/app/player-expert/service/amalia-player-service.ts
  • src/app/player-expert/asset-details/asset-details.component.ts

1) Delivery model: single bundled JS

Player Expert expects Amalia to be available as a single bundle served by the host app:

  • assets/amalia-<version>.min.js

Where <version> matches the installed dependency version (@ina/amalia) in the host.

In Amalia core, the bundle is produced under:

  • dist/amalia/amalia-<version>.min.js

The bundle is generated by concatenating Angular build artifacts (runtime.js, polyfills.js, scripts.js, main.js).


2) Runtime loading strategy (Player Expert)

Player Expert loads the script dynamically and only once.

Behavior:

  • compute the Amalia version from package.json dependencies
  • check if a script already exists:
    • script[src="assets/amalia-<version>.min.js"]
  • if missing:
    • create <script type="module" src="assets/amalia-<version>.min.js">
    • append it to document.body

This avoids double-registering custom elements.


3) Player creation in the host

Player Expert creates the player element programmatically.

3.1 amalia-player

Attributes set by Player Expert:

  • player-id: logical identifier used to connect plugins to the right player instance
  • id: ajs-<playerId>
  • type: video or audio
  • config: JSON string (stringified configuration)
  • class: uses 'timebar'

3.2 Control bar

Player Expert always appends a control bar to the player:

  • <amalia-control-bar player-id="<playerId>">

4) Configuration built by Player Expert

The configuration is built by Player Expert in AmaliaPlayerService.configurePlayer(...).

Key points:

  • uses a different base config for video vs audio

    • VideoPlayerConfig

      VideoPlayerConfig (example)
      export class VideoPlayerConfig {
        tcOffset = 0;
        player = {
          backwardsSrc: '',
          src: '',
          autoplay: false,
          hls: {
            enable: true,
            config: {
              maxBufferLength: 12,
              maxMaxBufferLength: 60,
              backBufferLength: 0,
              enableWorker: false
            }
          },
          crossOrigin: 'anonymous',
          media: 'VIDEO',
        };
      
        thumbnail = {
          baseUrl: '',
          enableThumbnail: true,
          tcParam: 'start'
        };
        dataSources = [];
        debug = true;
        logLevel = "debug";
        pluginsConfiguration = {
          'CONTROL_BAR-PLAYER': {
            data: [
              {
                label: 'Barre de progression',
                control: 'progressBar',
                priority: 1
              },
              {
                label: "Télécharger",
                control: "download",
                icon: "download",
                zone: 1,
                order: 1,
                priority: 5,
                key: 'd',
              },
              /*{
                "label": "Sous titres",
                "control": "subtitles",
                "icon": "subtitle",
                "zone": 3,
                "priority": 3
              },*/
              {
                label: 'Playback rate custom steps',
                control: 'playbackRateCustomSteps'
              },
              {
                label: 'Playback rate steps',
                control: 'playbackRateSteps'
              },
              {
                label: 'Capture',
                control: 'download',
                icon: 'screenshot',
                key: 'c',
                zone: 1,
                order: 2,
                data: { 'tcParam': 'start', 'href': '' },
                priority: 2
              },
              {
                label: 'Playback Rate',
                control: 'playbackRate',
                zone: 1,
                priority: 3,
                order: 3
              },
              {
                label: 'Aller au début du média',
                icon: 'backward-start',
                control: 'backward-start',
                zone: 2,
                priority: 5,
                key: 'Home',
                notInMenu: true
              },
              {
                label: 'Retour rapide',
                icon: 'backward',
                control: 'backward',
                zone: 2,
                priority: 3,
                key: 'Shift + ArrowLeft'
              },
              {
                label: 'Retour ralenti',
                icon: 'slow-backward',
                control: 'slow-backward',
                zone: 2,
                priority: 4,
                key: 'Alt + ArrowLeft',
                notInMenu: true
              },
              {
                label: 'Retour 5 secondes par 5 secondes',
                icon: 'backward-5seconds',
                control: 'backward-5seconds',
                zone: 2,
                priority: 2,
                key: 'Control + ArrowLeft'
              },
              {
                label: 'Retour image par image',
                icon: 'backward-frame',
                control: 'backward-frame',
                zone: 2,
                priority: 3,
                key: 'ArrowLeft'
              },
              {
                label: 'Pause / Lire',
                control: 'playPause',
                zone: 2,
                priority: 2,
                key: 'espace'
              },
              {
                label: 'Avance image par image',
                icon: 'forward-frame',
                control: 'forward-frame',
                zone: 2,
                priority: 3,
                key: 'ArrowRight'
              },
              {
                label: 'Avance 5 secondes par 5 secondes',
                icon: 'forward-5seconds',
                control: 'forward-5seconds',
                zone: 2,
                priority: 2,
                key: 'Control + ArrowRight'
              },
              {
                label: 'Avance ralentie',
                icon: 'slow-forward',
                control: 'slow-forward',
                zone: 2,
                priority: 4,
                key: 'Alt + ArrowRight',
                notInMenu: true
              },
              {
                label: 'Avance rapide',
                icon: 'forward',
                control: 'forward',
                zone: 2,
                priority: 3,
                key: 'Shift + ArrowRight'
              },
              {
                label: 'Aller à la fin du média',
                icon: 'forward-end',
                control: 'forward-end',
                zone: 2,
                priority: 5,
                key: 'End',
                notInMenu: true
              },
              {
                label: 'Désactiver le son',
                control: 'volume',
                zone: 3,
                priority: 2,
                key: 'm',
                data: { 'channelMergeVolume': false, 'channelMergerNode': '' },
              },
              {
                label: 'Plein écran',
                control: 'toggleFullScreen',
                icon: 'fullscreen',
                zone: 3,
                priority: 2,
                key: 'f'
              },
              {
                label: 'Aspect ratio',
                control: 'aspectRatio',
                zone: 3,
                priority: 5,
                key: 'a'
              },
              {
                label: 'Figer',
                control: 'pinControls',
                icon: 'pin',
                zone: 3,
                priority: 4,
                key: 'p',
              },
              {
                label: 'Afficher les vitesses',
                control: 'displaySlider',
                icon: 'slider',
                zone: 3,
                priority: 5,
                key: 'v',
              },
              {
                label: 'Plus d\'options',
                control: 'menu',
                icon: 'dots',
                zone: 3,
                priority: 3,
                key: 'r'
              }
      
            ],
            pinnedControls: true,
          }
        };
      }
    • AudioPlayerConfig

      AudioPlayerConfig (example)
      export class AudioPlayerConfig {
        tcOffset = 0;
        player = {
          backwardsSrc: '',
          src: '',
          autoplay: false,
          hls: {
            enable: true,
            config: {
              maxBufferLength: 12,
              maxMaxBufferLength: 60,
              backBufferLength: 0,
              enableWorker: false
            }
          },
          crossOrigin: 'anonymous',
          media: 'AUDIO',
        };
      
        thumbnail = {
          baseUrl: '',
          enableThumbnail: false,
          tcParam: 'start'
        };
      
        dataSources = [];
        debug = true;
        logLevel = "debug";
      
        pluginsConfiguration = {
          'CONTROL_BAR-PLAYER': {
            data: [
              {
                label: 'Barre de progression',
                control: 'progressBar',
                priority: 1
              },
              {
                label: "Télécharger",
                control: "download",
                icon: "download",
                zone: 1,
                order: 2,
                data: {
                  href: ""
                },
                priority: 5,
                key: 'd',
              },
              /*{
                "label": "Sous titres",
                "control": "subtitles",
                "icon": "subtitle",
                "zone": 3,
                "priority": 3
              },*/
              {
                label: 'Playback rate custom steps',
                control: 'playbackRateCustomSteps'
              },
              {
                label: 'Playback rate steps',
                control: 'playbackRateSteps'
              },
              {
                label: 'Playback Rate',
                control: 'playbackRate',
                zone: 1,
                priority: 3,
                order: 3
              },
              {
                label: 'Aller au début du média',
                icon: 'backward-start',
                control: 'backward-start',
                zone: 2,
                priority: 5,
                key: 'Home',
                notInMenu: true
              },
              {
                label: 'Retour rapide',
                icon: 'backward',
                control: 'backward',
                zone: 2,
                priority: 3,
                key: 'Shift + ArrowLeft'
              },
              {
                label: 'Retour ralenti',
                icon: 'slow-backward',
                control: 'slow-backward',
                zone: 2,
                priority: 4,
                key: 'Alt + ArrowLeft',
                notInMenu: true
              },
              {
                label: 'Retour 1 seconde par 1 seconde',
                icon: 'backward-second',
                control: 'backward-second',
                zone: 2,
                priority: 2,
                key: 'ArrowLeft'
              },
              {
                label: 'Pause / Lire',
                control: 'playPause',
                zone: 2,
                priority: 2,
                key: 'espace'
              },
              {
                label: 'Avance 1 seconde par 1 seconde',
                icon: 'forward-second',
                control: 'forward-second',
                zone: 2,
                priority: 2,
                key: 'ArrowRight'
              },
              {
                label: 'Avance ralentie',
                icon: 'slow-forward',
                control: 'slow-forward',
                zone: 2,
                priority: 4,
                key: 'Alt + ArrowRight',
                notInMenu: true
              },
              {
                label: 'Avance rapide',
                icon: 'forward',
                control: 'forward',
                zone: 2,
                priority: 3,
                key: 'Shift + ArrowRight'
              },
              {
                label: 'Aller à la fin du média',
                icon: 'forward-end',
                control: 'forward-end',
                zone: 2,
                priority: 5,
                key: 'End',
                notInMenu: true
              },
              {
                label: 'Désactiver le son',
                control: 'volume',
                zone: 3,
                priority: 2,
                key: 'm',
                data: { 'channelMergeVolume': false, 'channelMergerNode': '' },
              },
              {
                label: 'Plein écran',
                control: 'toggleFullScreen',
                icon: 'fullscreen',
                zone: 3,
                priority: 2,
                key: 'f'
              },
              {
                label: 'Figer',
                control: 'pinControls',
                icon: 'pin',
                zone: 3,
                priority: 4,
                key: 'p',
              },
              {
                label: 'Afficher les vitesses',
                control: 'displaySlider',
                icon: 'slider',
                zone: 3,
                priority: 5,
                key: 'v',
              },
              {
                label: 'Plus d\'options',
                control: 'menu',
                icon: 'dots',
                zone: 3,
                priority: 3,
                key: 'r'
              }
            ],
            pinnedControls: true,
          }
        };
      }
  • sets core fields:

    • player.src
    • player.hls.config.startPosition
    • tcOffset
    • thumbnail.baseUrl (e.g. ...?width=320)
    • loadMetadataOnDemand = true
    • dataSources = [] initially, populated later

Control bar configuration is also customized by removing controls depending on media type.


5) Plugins used in Player Expert

Player Expert creates plugin containers via AmaliaPlayerService.

5.1 Timeline

Element: amalia-timeline

Player Expert config shape:

  • metadataIds
  • data.resourceType (stock | flux)
  • data.tcIn / data.duration for slicing
  • data.tvDaysEnabled (optional)

5.2 Transcription

Element: amalia-transcription

Player Expert config includes:

  • metadataIds
  • data.resourceType (stock | flux)
  • data.tcIn / data.duration for slicing
  • UI flags (progress bar, autoscroll, etc.)

5.3 Subtitles

Element: amalia-subtitles

Similar to transcription, but for subtitle metadata.

5.4 Storyboard

Element: amalia-storyboard

Player Expert passes:

  • data.baseUrl (imagettes endpoint)
  • data.tcParam (e.g. start)
  • theme + layout (items per line)

5.5 Histogram

Element: amalia-histogram

Used for audio analysis/visualization, with a dedicated plugin configuration.

5.6 Time bar

Element: amalia-time-bar

Player Expert sets:

  • data.timeFormat:
    • f for stock
    • hours for flux
  • data.theme = 'outside'
  • optional first_tc for stock

5.7 Annotation

Element: amalia-annotation

Created with a richer config (categories/keywords, UI behavior).

Player Expert resolves autocomplete data through its backend before creating the plugin.


6) Metadata pipeline (Player Expert)

Player Expert builds a list of metadata endpoints (listOfMetadata) and then injects them into Amalia as dataSources.

6.1 URL conventions

Player Expert frequently includes:

  • format=AMALIA (or format=amalia-mot for word-based formats)
  • plugin=<PLUGIN_NAME> (e.g. TIMELINE, ANNOTATION)
  • clientId=<id> (namespacing per plugin instance)

Concrete examples from Player Expert:

Timelines (Flux)

  • /segments/flux?channel=<channel>&startDate=<from>&endDate=<to>&format=AMALIA&plugin=TIMELINE&clientId=timelines

Timelines (Stock)

  • /segments/stock?itemBusinessIdentifier=<id>&tcin=<tcIn>&tcout=<tcOut>&format=AMALIA&plugin=TIMELINE&clientId=timelines

Documents liés → Timelines datasource

  • /px/item/instances-segments?item=<businessId>&tcin=<...>&tcout=<...>&clientId=DOCUMENTS_LIES-

Subtitles

  • /search/plugin?pluginName=subtitles&format=amalia-mot&clientId=subtitles<params>

Transcriptions

  • /search/plugin?pluginName=transcriptions&format=amalia-mot&clientId=transcription-<algo><params>&algoname=<algo>

Annotation sources

  • /segments/flux?...&format=AMALIA&plugin=ANNOTATION&clientId=annotations
  • /segments/stock?...&format=AMALIA&plugin=ANNOTATION&clientId=annotations

6.2 Headers

Player Expert sets Authorization headers on datasources:

  • Authorization: Bearer <token>

Some endpoints are fetched with:

  • Accept: application/x-msgpack

Amalia’s MetadataManager has support for refreshing Authorization headers.


7) Loading sequence / readiness (Player Expert)

Player Expert waits for multiple conditions before creating the full Amalia UI.

It uses polling helpers such as Utils.waitFor(...) with:

  • interval: 10ms
  • timeout: 30000ms

Example conditions in AssetDetailsComponent:

  • documentsLieComplete
  • pluginsInfoAreComplete() (documents liés + plugins from LDD + “extraits utilisateurs”)

Once complete, Player Expert:

  • reinitializes containers
  • (optionally) reopens last grid / plugins disposition

8) Events used by Player Expert

Player Expert defines and listens/emits these namespaced event constants:

  • Player lifecycle:

    • ina.player.PLAYING
    • ina.player.METADATA_LOADED
    • ina.player.CONTROL_BAR_TOGGLED
  • Annotation workflow:

    • ina.annotation.ADD
    • ina.annotation.PATCH
    • ina.annotation.REMOVE
    • ina.annotation.EDITING
    • ina.annotation.EDITING.CANCELED
    • ina.OPEN_NOTILUS_MATERIAL
  • Contribution juridique integration:

    • ina.CONTRIBUTION_JURIDIQUE_ASK_FOR_CURRENT_TIME
    • ina.CONTRIBUTION_JURIDIQUE_GET_CURRENT_TIME
    • ina.CONTRIBUTION_JURIDIQUE_SET_CURRENT_TIME
    • ina.CONTRIBUTION_JURIDIQUE_ASK_FOR_DURATION
    • ina.CONTRIBUTION_JURIDIQUE_GET_DURATION
  • Timelines:

    • ina.TIMELINE_EXPORT_TV_DAYS

9) Troubleshooting

Custom element not recognized

If the host app template compilation complains about unknown elements:

  • Angular apps must enable CUSTOM_ELEMENTS_SCHEMA.

Script loaded multiple times

If the host injects the Amalia script multiple times, custom elements can fail to register.

Player Expert avoids this by checking for the existing <script> before appending.

Metadata requests failing (401/403)

Check:

  • Authorization: Bearer <token> header is present
  • token refresh strategy (Amalia MetadataManager refresh logic)
  • correct Accept header for msgpack endpoints

Appendix: relevant Player Expert files

  • src/app/player-expert/service/amalia-player-service.ts
  • src/app/player-expert/asset-details/asset-details.component.ts