VideohatiDocs
SDKs

Player

Official @videohati/player — the framework-free web player. Embed it with a script tag or install it from npm, then drive it with a small imperative API.

@videohati/player renders a protected Videohati video on any web page. It has no framework dependency, an Arabic-first RTL interface, and a session-bound playback flow with a heartbeat and an on-video watermark. It is a deterrent, not DRM. Version 1.0.0.

Every embed needs a sessionToken from a server-side POST /v1/playback/sessions — create it with @videohati/node or videohati/laravel. The API key must never reach the browser.

Script embed

Add the player script and a div carrying the video, project, and session token. The player auto-mounts on every [data-videohati-video-id] element and tears itself down when the page is hidden.

<script src="https://cdn.videohati.com/player.js" async></script>
<div
  data-videohati-video-id="01JZ9WV3N8GQ5T2M7K4C6XBARH"
  data-videohati-project-id="01JZ9WV3N8GQ5T2M7K4C6XBARF"
  data-videohati-session-token="<sessionToken>"
  data-videohati-autoplay="muted"
  data-videohati-width="640"
  data-videohati-height="360"
  data-videohati-lang="ar"
  data-videohati-viewer-text="Ahmed K."
></div>

Without a session token the player refuses to instantiate and shows "Missing session token".

AttributeWhat it sets
data-videohati-video-idThe video to play (required).
data-videohati-project-idThe project that owns the video (required).
data-videohati-session-tokenThe server-created session token (required).
data-videohati-autoplay"muted" autoplays muted; "true" tries unmuted; default off.
data-videohati-widthWidth in pixels.
data-videohati-heightHeight in pixels.
data-videohati-lang"ar" (default) or "en".
data-videohati-viewer-textText drawn into the watermark.
data-videohati-api-originAPI origin override (staging, local overrides).

npm

Install the package and create a player yourself when you need the instance — for example to call play() or subscribe to events.

npm install @videohati/player
import { Videohati } from "@videohati/player";

const player = Videohati.create({
  target: document.querySelector("#player")!,
  videoId: "01JZ9WV3N8GQ5T2M7K4C6XBARH",
  projectId: "01JZ9WV3N8GQ5T2M7K4C6XBARF",
  sessionToken,
  lang: "en",
});

player.on("state", (state) => console.log(state));
player.play();

Videohati.create(options) returns a Player.

OptionTypeWhat it does
targetHTMLElement | string (required)Element or selector the player renders into.
videoIdstring (required)The video to play.
projectIdstring (required)The project that owns the video.
sessionTokenstring (required)The server-created session token.
autoplayboolean | "muted""muted" autoplays muted; true tries unmuted; default false.
widthnumberWidth in pixels.
heightnumberHeight in pixels.
lang"ar" | "en"UI language. Defaults to Arabic.
viewerTextstringWatermark text; falls back to the session's viewer text.
apiOriginstringAPI origin override.

Player API

The instance exposes a small imperative API.

MethodWhat it does
play()Starts playback.
pause()Pauses playback.
seek(seconds)Jumps to a time, clamped to the video length.
setQuality(name)Selects a rendition by name (for example "720p") or "auto".
setLanguage(lang)Switches the UI language at runtime ("ar" / "en").
getState()Returns the current state.
on(event, listener)Subscribes to an event.
off(event, listener)Removes a listener.
destroy()Tears down the player and ends the session.

Call destroy() when you remove the player from the page — it ends the session and stops the heartbeat. The script embed does this for you on page hide.

States

getState() and the state event return one of:

idle · loading · playing · paused · ended · stopped · error.

stopped is a terminal state the player enters when the session is revoked or the watermark is tampered with.

Events

Subscribe with player.on(name, listener).

EventPayloadFires when
statethe new statePlayback state changed.
error{ code, message }Playback failed or the session ended.
timeupdate{ currentTime, duration }The playhead moved.
qualitychange{ quality }The active rendition changed.
watermarkTamperedThe on-video watermark was tampered with.
captureBlockedAn in-page capture attempt was blocked.

The last two fire when the on-video watermark is tampered with or a capture attempt is blocked — you can log these.

Language and direction

The interface text defaults to Arabic. Pass lang: "en" (or data-videohati-lang="en") for English. The strings are the only thing language changes: the player chrome itself is always left-to-right, like every mainstream player, so it lays out correctly inside a right-to-left page.

When the embed passes no lang, the player follows the project's Player UI language setting (dashboard → project → Player), including its "match the viewer's browser" option.

Customize the player

Every embed of a project follows the project's player settings — no embed-code change needed. Open dashboard → project → Player to set:

  • UI language and font — Arabic, English, or the viewer's browser language; a font from a self-hosted set (loaded from the Videohati CDN, never a third-party origin).
  • Primary color — the progress bar, big play button, and menu highlights.
  • Controls — show or hide each control (play/pause, ±10s, progress, time, volume, quality, settings, fullscreen). Picture-in-picture and AirPlay exist as opt-ins and stay off by default: pop-out and cast playback bypass the watermark and capture protections.
  • Playback speeds — the exact speed-menu entries (0.1×–16×, up to 12, 1.0× always included).
  • Watchtime heatmap — a public watch-density curve above the progress bar, shown once a video has at least 30 viewing sessions.
  • Resumable position (on by default), compact controls, and custom player CSS scoped to .vh-player.

Settings apply to playback sessions created after the save; an embed that passes an explicit lang keeps it.

Content Security Policy

Allow the player's two origins. connect-src covers the API and playback traffic; media-src blob: covers the streamed media; script-src is needed only for the script embed.

connect-src https://api.videohati.com https://cdn.videohati.com;
media-src blob:;
script-src https://cdn.videohati.com;