Aller au contenu principal

Player web avec le SDK JavaScript

Intégrer un player vidéo dans le navigateur avec @videas/sdk côté serveur pour émettre un jeton, puis hls.js pour lire le flux HLS signé.

Ce guide câble un player <video> fonctionnel avec @videas/sdk côté serveur et hls.js dans le navigateur. Lisez d’abord Comment fonctionne la lecture — la règle clé : émettre le jeton nécessite votre clé secrète et doit tourner côté serveur, tandis que le navigateur ne manipule que le jeton.

Étape 1 — émettre un jeton de player (serveur)

Votre serveur détient la clé sk_ et expose un petit endpoint qui ne renvoie que des données sûres pour le navigateur. Ici avec Express :

// server.ts — tourne sur VOTRE serveur, jamais envoyé au navigateur
import express from 'express'
import { createVideasClient } from '@videas/sdk'

const app = express()
const videas = createVideasClient({ apiKey: process.env.VIDEAS_API_KEY! })

app.get('/api/player/:uid', async (req, res) => {
  const player = await videas.videos.getPlayer(req.params.uid)
  // Ne transmettez QUE des champs sûrs à la page — jamais la clé sk_.
  res.json({
    assetId: req.params.uid,
    playbackToken: player.playback_token, // sûr à exposer ; valide 4h
    poster: player.items[0]?.asset.thumbnail_url ?? null,
    config: player.player_config, // auto_play, loop, theme, …
  })
})

L’API renvoie des champs URL absolus (comme thumbnail_url), donc poster est utilisable tel quel.

player_config est résolu à chaque émission de jeton, pas figé dans votre code : le thème, la couleur d’accent et le logo que votre client change dans Videas s’appliquent sans que vous redéployiez. C’est ce qui fait qu’une intégration livrée une fois continue sans vous — voir Développeurs et intégrateurs.

Étape 2 — résoudre le flux et lire (navigateur)

Dans le navigateur, vous n’instanciez pas le SDK (cela nécessiterait la clé sk_). Appelez POST /playback/info/ directement avec le jeton, puis fournissez le manifeste HLS à un player.

<video id="player" controls playsinline style="width: 100%"></video>

<script type="module">
  const API = 'https://api.videas.com/api/external/v1'

  // 1. Récupérer le jeton sûr depuis votre propre serveur (étape 1).
  const { assetId, playbackToken, poster } =
    await (await fetch('/api/player/vid_123')).json()

  // 2. Échanger le jeton contre les URLs signées (aucune clé API ici).
  const info = await (await fetch(`${API}/playback/info/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ asset_id: assetId, playback_token: playbackToken }),
  })).json()

  const video = document.getElementById('player')

  // 3. Respecter le gate du portefeuille.
  if (info.blocked) {
    video.outerHTML = '<p>Cette vidéo est temporairement indisponible.</p>'
    throw new Error('lecture bloquée')
  }

  if (poster) video.poster = poster

  // 4. Lire le manifeste HLS — natif sur Safari/iOS, hls.js ailleurs.
  const src = info.hls_url?.url
  if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = src
  } else {
    const { default: Hls } = await import('https://cdn.jsdelivr.net/npm/hls.js@1/dist/hls.mjs')
    if (Hls.isSupported()) {
      const hls = new Hls()
      hls.loadSource(src)
      hls.attachMedia(video)
    } else if (info.source_url) {
      video.src = info.source_url.url // dernier repli : MP4 progressif
    }
  }

  // 5. Reprendre là où le spectateur s'était arrêté, si connu.
  if (info.resume_position_seconds) {
    video.currentTime = info.resume_position_seconds
  }
</script>

Voilà un player pleinement fonctionnel. Tout ce qui suit est du peaufinage optionnel.

Rafraîchir les URLs expirées

Les URLs signées expirent (voir info.hls_url.expires_at) bien avant le jeton de 4 h. Pour les sessions longues, relancez l’étape 2 à l’approche de l’expiration — vous pouvez continuer à réutiliser le même playbackToken :

async function resolve(assetId, token) {
  const res = await fetch(`${API}/playback/info/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ asset_id: assetId, playback_token: token }),
  })
  return res.json()
}

Ce n’est que lorsque le jeton lui-même expire (après 4 h) que vous rappelez votre serveur pour en émettre un nouveau.

Chapitres & vignettes de la barre de progression

info.chapters est une liste prête à l’emploi ; affichez des marqueurs sur votre barre de progression :

for (const chapter of info.chapters ?? []) {
  console.log(chapter.start_time, chapter.title) // + chapter.thumbnail_url
}

info.preview_sprites_url est une planche de sprites pour les vignettes au survol ; branchez-la dans le plugin de vignettes de votre player s’il gère les planches de sprites.

Alternative — tout résoudre côté serveur

Si vous rendez les pages côté serveur et lisez immédiatement, vous pouvez éviter la séparation et utiliser le helper en un appel, qui fait les deux étapes avec votre clé :

// Côté serveur uniquement — nécessite la clé sk_.
const info = await videas.videos.resolvePlayback('vid_123')
// Injectez info.hls_url.url dans votre template.

Comme les URLs signées sont à courte durée, résolvez juste avant de servir la page, et attendez-vous à ré-résoudre pour les sessions longues (c’est exactement pourquoi l’approche « jeton vers le navigateur » ci-dessus passe mieux à l’échelle).

Étapes suivantes