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".
| Attribute | What it sets |
|---|---|
data-videohati-video-id | The video to play (required). |
data-videohati-project-id | The project that owns the video (required). |
data-videohati-session-token | The server-created session token (required). |
data-videohati-autoplay | "muted" autoplays muted; "true" tries unmuted; default off. |
data-videohati-width | Width in pixels. |
data-videohati-height | Height in pixels. |
data-videohati-lang | "ar" (default) or "en". |
data-videohati-viewer-text | Text drawn into the watermark. |
data-videohati-api-origin | API 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/playerimport { 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.
| Option | Type | What it does |
|---|---|---|
target | HTMLElement | string (required) | Element or selector the player renders into. |
videoId | string (required) | The video to play. |
projectId | string (required) | The project that owns the video. |
sessionToken | string (required) | The server-created session token. |
autoplay | boolean | "muted" | "muted" autoplays muted; true tries unmuted; default false. |
width | number | Width in pixels. |
height | number | Height in pixels. |
lang | "ar" | "en" | UI language. Defaults to Arabic. |
viewerText | string | Watermark text; falls back to the session's viewer text. |
apiOrigin | string | API origin override. |
Player API
The instance exposes a small imperative API.
| Method | What 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).
| Event | Payload | Fires when |
|---|---|---|
state | the new state | Playback state changed. |
error | { code, message } | Playback failed or the session ended. |
timeupdate | { currentTime, duration } | The playhead moved. |
qualitychange | { quality } | The active rendition changed. |
watermarkTampered | — | The on-video watermark was tampered with. |
captureBlocked | — | An 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;