@vioraio/player
v2.9.2
Published
Viora Player — HLS video player for education (VOD & live classrooms)
Maintainers
Readme
Viora Player
@vioraio/player — HLS video player for education platforms (schools, universities, online classrooms).
Part of the Viora family by vioraio:
| Product | Role | | --- | --- | | Viora Player | HLS playback (this package) | | Viora Stream | VOD / streaming (coming) | | Viora Live | Online classroom (coming) |
Install
pnpm add @vioraio/playerPeer deps: react, react-dom, hls.js, framer-motion, clsx.
Styles (Tailwind v4 hosts — recommended)
Do not import @vioraio/player/styles.css inside apps that already run Tailwind
(HeroUI / Next.js). That file ships a second theme layer and breaks host styles.
Scan the package from your global CSS instead:
@import "tailwindcss";
@source "../node_modules/@vioraio/player/dist/**/*.{js,cjs,mjs}";Styles (no Tailwind)
import "@vioraio/player/styles.css";Usage
Single video
import { VioraPlayer } from "@vioraio/player";
<VioraPlayer
userId={user?.id}
sources={[{ id: 1, title: "...", poster: "...", data: { type: "hls", src: "https://.../master.m3u8" } }]}
/>data is { type: "hls", src } or { type: "file", src } (a progressive mp4/webm — no quality menu, no offline download).
Playlist
<VioraPlayer
userId={user?.id}
playerName="شهریار پلیر"
sources={teasers.map((t) => ({ id: t.id, title: t.title, poster: t.cover, data: { type: "hls", src: t.src } }))}
activeSourceId={activeId}
onSourceSelect={(item) => setActiveId(item.id)}
playlistTitle="تیزرها"
autoPlayNext
loopPlaylist
/>With watch-session persistence
import { VioraPlayer, type WatchSessionFlushPayload } from "@vioraio/player";
<VioraPlayer
userId={studentId}
sources={[{ id: videoId, progressKey: subjectId, watchedDuration: watched, data: { type: "hls", src } }]}
onSaveWatchSession={async (payload: WatchSessionFlushPayload) => {
await api.post(`/course/video/watch/session/${videoId}`, payload);
}}
/>Theme & controls
<VioraPlayer
sources={sources}
theme={{
colors: {
primary: "#006FEE",
secondary: "#17C964",
danger: "#F31260",
surface: "rgba(0,0,0,0.82)",
foreground: "#ffffff",
overlay: "rgba(0,0,0,0.55)",
},
}}
controls={{ download: false }}
/>When the control row is narrow (phones), enabled speed + quality + download fold into one تنظیمات control.
Behavior
- Quality. A quality picked by hand stays picked: the player retries that rendition through network trouble instead of stepping down, and the choice is remembered per user. Only خودکار adapts, starting from the last measured bandwidth.
- Fullscreen. Where the browser supports element fullscreen (desktop, Android, iPad) the player fills the screen with its own controls, and phones turn to landscape. iPhone has no element fullscreen: there the video goes to the system's own fullscreen player — the only real fullscreen an iPhone page gets (the whole screen, no Safari bars, landscape) — with Apple's controls while it lasts. Where neither exists or a WebView never settles the request, the player covers the viewport itself, above the host's sticky headers and bars, and locks page scroll.
- Lock screen / background. Playback pauses when the page is hidden and resumes when it returns — only if the player (or the system, e.g. iOS on lock) paused it. On return a fresh frame is decoded so the picture is never left black, and a stuck pipeline is recovered.
- Offline. A downloaded quality is stored with everything playback needs — playlists, segments, init segments, AES-128 keys and alternate audio — in the Cache API, and plays with no network at all, even when the stream URL carries a new token.
- Gestures. On touch screens: tap toggles the controls, double-tap on either side seeks ±15 s (repeated taps add up).
- Keyboard.
Space/Kplay/pause ·←/→seek ·Mmute ·Ffullscreen ·C/Xfaster/slower ·Iinfo ·Escclose/exit.
Props
Source & session
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| sources | VioraSource[] | — | Playable entries: id, data, optional title, poster, posterFallback, watchedDuration, progressKey, durationLabel |
| defaultPlay | number | first item | Initial source: matched by id, else a 0-based index |
| activeSourceId | number | — | Controlled active source |
| onSourceSelect | (item, index) => void | — | Selection / skip / auto-next |
| autoPlayOnSourceChange | boolean | false | Start playing when the active source changes from outside |
| prepareState | "loading" \| "error" \| "ready" | "ready" | Host-driven ready state |
| userId | number \| null | anonymous | Scopes stored progress and settings |
| onSaveWatchSession | (payload) => Promise<void> | — | Server flush; without it progress stays in localStorage |
Branding & chrome
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| playerName | string | ویورا پلیر | Info panel brand name |
| showTitle | boolean | true | Show title in the playing top bar |
| theme | VioraPlayerTheme | defaults | 6 color tokens |
| controls | VioraPlayerControlsConfig | all true | Toggle play, seek, skip, mute, fullscreen, speed, quality, download, info, playlist |
Playback
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| playbackRate | number | from settings / 1 | Rate 0.5–3 (0.5 steps) |
| defaultQuality | number \| "auto" | from settings / "auto" | Initial quality |
| onEnded | () => void | — | Fired when the current video ends |
| pauseWhenOutOfView | boolean | true | Pause when scrolled fully out of view; resume when visible again |
Playlist
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| playlistTitle | string | لیست پخش | Drawer header title |
| showPlaylistButton | boolean | true with 2+ sources | Show playlist chrome button |
| playlistOpen | boolean | — | Controlled drawer open |
| defaultPlaylistOpen | boolean | false | Uncontrolled initial open |
| onPlaylistOpenChange | (open) => void | — | Drawer open changes |
| autoPlayNext | boolean | false | Auto-play next after countdown |
| showAutoPlayNext | boolean | true with 2+ sources | Show the in-drawer toggle (hide = fixed by autoPlayNext) |
| onAutoPlayNextChange | (enabled) => void | — | Viewer toggled auto-next |
| loopPlaylist | boolean | false | Wrap last → first |
| autoOpenPlaylistOnEnded | boolean | true with 2+ sources | Open drawer when a video ends |
Deprecated (still supported)
id, title, poster, posterFallback, data, watchedDuration, progressKey, videoSubjectId → use sources.
studentId → userId. playlist → sources. activePlaylistId → activeSourceId. defaultActivePlaylistId → defaultPlay. onPlaylistItemSelect → onSourceSelect.
Develop
pnpm install
pnpm build
pnpm typecheckRelease
Push to main publishes @vioraio/player to npm automatically.
- If the current
package.jsonversion is already on npm, CI bumps the patch version, commits it ([skip ci]), then publishes. - Requires repo secret
NPM_TOKEN(Granular Access Token with publish for@vioraio, bypass 2FA).
License
MIT
