@scarlett-player/vue
v0.4.0
Published
Vue 3 component wrapper for Scarlett Player
Maintainers
Readme
@scarlett-player/vue
Vue 3 component wrapper for Scarlett Player.
Installation
npm install @scarlett-player/vue @scarlett-player/coreQuick Start
Basic Component Usage
<template>
<div class="video-container">
<ScarlettPlayer
ref="playerRef"
:src="videoUrl"
:autoplay="false"
:plugins="plugins"
@ready="onPlayerReady"
@play="onPlay"
@pause="onPause"
@timeupdate="onTimeUpdate"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ScarlettPlayerComponent } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { createNativePlugin } from '@scarlett-player/native';
import { uiPlugin } from '@scarlett-player/ui';
const playerRef = ref();
const videoUrl = ref('https://example.com/video.m3u8');
const plugins = [
createHLSPlugin(),
createNativePlugin(),
uiPlugin({
theme: {
accentColor: '#e50914',
},
}),
];
function onPlayerReady(player) {
console.log('Player ready!', player);
}
function onPlay() {
console.log('Playing');
}
function onPause() {
console.log('Paused');
}
function onTimeUpdate({ currentTime, duration }) {
console.log(`Time: ${currentTime}s / ${duration}s`);
}
// Control player programmatically
async function playVideo() {
await playerRef.value?.play();
}
function pauseVideo() {
playerRef.value?.pause();
}
function seekTo(time: number) {
playerRef.value?.seek(time);
}
</script>
<style scoped>
.video-container {
width: 100%;
max-width: 1280px;
aspect-ratio: 16 / 9;
}
</style>Using the Composable API
For more control and reactive state management:
<template>
<div>
<div ref="containerRef" class="video-container"></div>
<div class="controls">
<button @click="play" :disabled="playing">Play</button>
<button @click="pause" :disabled="!playing">Pause</button>
<button @click="toggleFullscreen">Fullscreen</button>
<div class="time">
{{ formatTime(currentTime) }} / {{ formatTime(duration) }}
</div>
<input
type="range"
:value="currentTime"
:max="duration"
@input="seek($event.target.value)"
/>
<div class="volume">
<button @click="setMuted(!muted)">
{{ muted ? 'Unmute' : 'Mute' }}
</button>
<input
type="range"
:value="volume"
min="0"
max="1"
step="0.1"
@input="setVolume($event.target.value)"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useScarlettPlayer } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { createNativePlugin } from '@scarlett-player/native';
import { uiPlugin } from '@scarlett-player/ui';
const containerRef = ref<HTMLElement | null>(null);
const {
player,
isReady,
playing,
paused,
currentTime,
duration,
volume,
muted,
fullscreen,
play,
pause,
seek,
setVolume,
setMuted,
toggleFullscreen,
} = useScarlettPlayer({
container: containerRef,
src: 'https://example.com/video.m3u8',
plugins: [
createHLSPlugin(),
createNativePlugin(),
uiPlugin(),
],
autoInit: true,
});
function formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
</script>
<style scoped>
.video-container {
width: 100%;
max-width: 1280px;
aspect-ratio: 16 / 9;
background: #000;
}
.controls {
margin-top: 1rem;
display: flex;
gap: 1rem;
align-items: center;
}
</style>Component Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| src | string | - | Video source URL |
| poster | string | - | Poster image URL |
| autoplay | boolean | false | Auto-play video on load |
| loop | boolean | false | Loop playback |
| volume | number | 1.0 | Initial volume (0-1) |
| muted | boolean | false | Start muted |
| logLevel | 'debug' \| 'info' \| 'warn' \| 'error' | 'warn' | Console log level |
| plugins | Plugin[] | [] | Scarlett Player plugins |
| options | PlayerOptions | {} | Additional player options |
Component Events
| Event | Payload | Description |
|-------|---------|-------------|
| ready | ScarlettPlayer | Player initialized and ready |
| play | - | Playback started |
| pause | - | Playback paused |
| seeking | { time: number } | Seeking to new time |
| seeked | - | Seek completed |
| timeupdate | { currentTime: number, duration: number } | Time updated |
| volumechange | { volume: number, muted: boolean } | Volume/mute changed |
| ratechange | { rate: number } | Playback rate changed |
| ended | - | Playback ended |
| error | Error | Error occurred |
| loaded | any | Media loaded |
| loadedmetadata | any | Metadata loaded |
| qualitychange | { quality: string, auto: boolean } | Quality level changed |
| qualitylevels | any | Quality levels available |
| fullscreenchange | { fullscreen: boolean } | Fullscreen state changed |
| destroy | - | Player destroyed |
Component Methods (via ref)
Access player methods via template ref:
<template>
<ScarlettPlayer ref="playerRef" />
</template>
<script setup>
const playerRef = ref();
// Access methods
playerRef.value.play();
playerRef.value.pause();
playerRef.value.seek(30);
playerRef.value.setVolume(0.5);
playerRef.value.setMuted(true);
playerRef.value.setQuality(2);
playerRef.value.requestFullscreen();
</script>Available Methods
play()- Start playbackpause()- Pause playbackseek(time: number)- Seek to time in secondsload(src: string)- Load new sourcesetVolume(volume: number)- Set volume (0-1)setMuted(muted: boolean)- Set muted statesetPlaybackRate(rate: number)- Set playback speedgetQualities()- Get available quality levelssetQuality(index: number)- Set quality level (-1 for auto)getCurrentQuality()- Get current quality indexrequestFullscreen()- Enter fullscreenexitFullscreen()- Exit fullscreentoggleFullscreen()- Toggle fullscreenrequestAirPlay()- Show AirPlay pickerrequestChromecast()- Request Chromecast sessionstopCasting()- Stop castingseekToLive()- Seek to live edge (for live streams)getState()- Get current state snapshotgetPlugin(name: string)- Get plugin instanceregisterPlugin(plugin: Plugin)- Register new plugindestroy()- Destroy player instance
Composable API
The useScarlettPlayer composable provides reactive state and methods:
const {
// Instance
player,
isReady,
error,
// Reactive state
playing,
paused,
currentTime,
duration,
volume,
muted,
bufferedAmount,
fullscreen,
live,
progress,
isBuffering,
// Methods
init,
play,
pause,
seek,
load,
setVolume,
setMuted,
requestFullscreen,
exitFullscreen,
toggleFullscreen,
getQualities,
setQuality,
getCurrentQuality,
} = useScarlettPlayer(options);Global Plugin Installation
You can register the component globally:
import { createApp } from 'vue';
import { ScarlettPlayerPlugin } from '@scarlett-player/vue';
import App from './App.vue';
const app = createApp(App);
app.use(ScarlettPlayerPlugin);
app.mount('#app');Then use it without importing:
<template>
<ScarlettPlayer :src="videoUrl" :plugins="plugins" />
</template>TypeScript Support
Full TypeScript support with exported types:
import type {
ScarlettPlayer,
PlayerOptions,
QualityLevel,
ScarlettPlugin,
EventName,
StateStore,
} from '@scarlett-player/vue';Examples
Custom Controls
<template>
<div class="player-wrapper">
<ScarlettPlayer
ref="playerRef"
:src="videoUrl"
:plugins="plugins"
@timeupdate="onTimeUpdate"
/>
<!-- Custom UI overlay -->
<div class="custom-controls">
<button @click="playPause">
{{ playing ? 'Pause' : 'Play' }}
</button>
<div class="progress-bar" @click="handleProgressClick">
<div class="progress-fill" :style="{ width: progressPercent + '%' }"></div>
</div>
<select @change="handleQualityChange">
<option value="-1">Auto</option>
<option
v-for="(quality, index) in qualities"
:key="index"
:value="index"
>
{{ quality.label }}
</option>
</select>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ScarlettPlayerComponent } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { uiPlugin } from '@scarlett-player/ui';
const playerRef = ref();
const videoUrl = ref('https://example.com/video.m3u8');
const playing = ref(false);
const currentTime = ref(0);
const duration = ref(0);
const qualities = ref([]);
const plugins = [
createHLSPlugin(),
uiPlugin({ hideDelay: 3000 }),
];
const progressPercent = computed(() => {
if (duration.value === 0) return 0;
return (currentTime.value / duration.value) * 100;
});
function onTimeUpdate({ currentTime: ct, duration: d }) {
currentTime.value = ct;
duration.value = d;
}
function playPause() {
if (playing.value) {
playerRef.value?.pause();
} else {
playerRef.value?.play();
}
playing.value = !playing.value;
}
function handleProgressClick(e: MouseEvent) {
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
const seekTime = percent * duration.value;
playerRef.value?.seek(seekTime);
}
function handleQualityChange(e: Event) {
const index = parseInt((e.target as HTMLSelectElement).value);
playerRef.value?.setQuality(index);
}
function loadQualities() {
qualities.value = playerRef.value?.getQualities() ?? [];
}
</script>Live Stream with DVR
<template>
<div>
<ScarlettPlayer
ref="playerRef"
:src="liveStreamUrl"
:autoplay="true"
:plugins="plugins"
@loadedmetadata="onMetadataLoaded"
/>
<button v-if="isLive" @click="goToLive">
Go to Live
</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ScarlettPlayerComponent } from '@scarlett-player/vue';
import { createHLSPlugin } from '@scarlett-player/hls';
import { uiPlugin } from '@scarlett-player/ui';
const playerRef = ref();
const liveStreamUrl = ref('https://example.com/live.m3u8');
const isLive = ref(false);
const plugins = [
createHLSPlugin({ lowLatencyMode: true }),
uiPlugin(),
];
function onMetadataLoaded(payload) {
isLive.value = payload.live ?? false;
}
function goToLive() {
playerRef.value?.seekToLive();
}
</script>Browser Support
- Chrome 80+
- Firefox 78+
- Safari 14+
- Edge 80+
License
MIT
Credits
Built with Scarlett Player - Modular. Extensible. Yours.
