OPlayer UI
Install
npm i @oplayer/core @oplayer/ui
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/ui@latest/dist/index.core.js"></script>
<!-- or just one -->
<!-- <script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.ui.js"></script> -->
Basic
const player = Player.make('#player', {
source: {
title: '君の名は',
src: 'https://oplayer.vercel.app/君の名は.mp4',
poster: 'https://cdn.jsdelivr.net/gh/shiyiya/QI-ABSL@master/o/poster.png'
}
})
.use([OUI()])
.create()
Full-Options
export type UiConfig = {
theme?: {
primaryColor?: string
watermark?: {
/** img or svg */
src: string
// make screenshot include watermark?
// set positioning here [top, left, right, bottom]
style?: Record<string, string>
attrs?: Record<string, string>
}
progress?: {
/**
* default: 'auto'
* auto: mobile->top pc->top
*/
position?: 'auto' | 'top' | 'center'
backward?: number
forward?: number
/**
* default: true
* work only top
*/
mini?: boolean
}
controller?: {
/**
* default: 'always'
*/
display?: 'always' | 'played'
/**
* default: if(source.title)
*/
header?: boolean | { back?: 'always' | 'fullscreen' }
/**
* default: true
*/
coverButton?: boolean
/**
* default: hover
*
*/
displayBehavior?: 'hover' | 'delay' | 'none'
/**
* default: 'none'
*/
slideToSeek?: 'none' | 'always' | 'long-touch'
}
setting?: {
/**
* default: 'auto'
* auto: mobile->top pc->top
*/
postion?: 'auto' | 'top' | 'bottom'
}
}
/**
* default: false
*/
autoFocus?: boolean
/**
* default: false
*/
screenshot?: boolean
/**
* default: true
* 全屏(如果不可用将会降级为网页全屏)
*/
fullscreen?: boolean
/**
* default: false
*/
pictureInPicture?: boolean
/**
* default: true
* Whether or not the device should rotate to landscape mode when the video
* enters fullscreen. Note that this behavior is based on an experimental
* browser API, and may not work on all platforms.
* Defaults to true.
*/
forceLandscapeOnFullscreen?: boolean
/**
* PC only - default: { focused: true }
*/
keyboard?: { focused?: boolean; global?: boolean }
/**
* default: ['2.0', '1.75', '1.25', '1.0', '0.75', '0.5']
*/
speeds?: string[]
subtitle?: Subtitle
/**
* default: ['loop', 'speed']
*/
settings?: (Setting | 'loop')[] | false
thumbnails?: Thumbnails
highlight?: {
color?: string
source?: Highlight[]
}
menu?: MenuBar[]
errorBuilder?: (error: ErrorPayload, target: HTMLDivElement, cb: (error: ErrorPayload) => void) => void
icons?: {
play?: string
pause?: string
volume?: [string, string] //on off
fullscreen?: [string, string]
pip?: [string, string]
setting?: string
screenshot?: string
playbackRate?: string
loop?: string
progressIndicator?: string
loadingIndicator?: string
next?: string
previous?: string
}
}
Thumbnails
// single
thumbnails = {
number: 100,
src: 'https://cdn.jsdelivr.net/gh/shiyiya/QI-ABSL@master/o/thumbnails.jpg'
}
// multipe & grid
thumbnails = {
src: ['1.jpg', '2,jpg'],
x: 10,
y: 10,
number: 192
}
Add custom menu
{
menu: [
{
name: 'Quality(清晰度)',
key: 'Quality', // for select
position: 'bottom', // or top
children: [
{
name: 'FHD',
default: true,
value: 'https://oplayer.vercel.app/君の名は.mp4'
},
{
name: 'HD',
value: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'
},
{
name: 'SD',
value: 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd'
}
],
onChange({ value }) {
player.changeQuality({ src: value })
}
}
]
}
//or
player.context.menu.register([])
Add custom setting
{
settings: [
'loop', // Build-in
'speed', // Build-in
{
// Custom
key: 'KEY',
type: 'selector', // or 'switcher'
name: 'name',
icon: 'icon',
children: [
{ name: 'a', value: 'a', default: true },
{ name: 'b', value: 'b', default: false }
],
onChange: ({ value }) => {
// do something
}
}
]
}
//or
player.context.menu.register([])
Highlight
{
highlight: {
color: '#fff', //default
source: [
{ time: 12, text: '谁でもいいはずなのに' },
{ time: 34, text: '夏の想い出がまわる' },
{ time: 58, text: 'こんなとこにあるはずもないのに' },
{ time: 88, text: '--终わり--' }
]
}
}
Methods
All methods on player.context.ui
// - Update subtitle
player.context.ui.subtitle.changeSource([])
// - Update highlight
player.context.ui.changHighlightSource([])
// - Update thumbnails
player.context.ui.changThumbnails('src')
// - Register menu
player.context.ui.menu.register({})
player.context.ui.menu.unregister('key')
player.context.ui.menu.select('key', 'index')
// - Register setting
player.context.ui.setting.register({})
player.context.ui.setting.unregister('key')
player.context.ui.setting.updateLabel('key', 'text')
player.context.ui.setting.select('key', 'index')
// - Display error,notice
player.emit('error', { message: 'msg', code: 'number' })
player.context.ui.notice('hello')
keyboard
↑
volume +10%↓
volume -10%←
seek -5s→
seek +5sspace
|enter
play or pauses
catch a screenshotf
toggle full-screenw
toggle full-screen webm
toggle mutec
toggle setting panel
watermark
const ui = OUI({
theme: {
watermark: {
src: '/your/path/logo.jpg',
style: {
position: 'absolute',
// want make screenshot include watermark?
// set positioning here, not css. [top, left, right, bottom]
top: '10px',
right: '10px',
width: '200px',
height: 'auto'
},
attrs: {
class: 'watermark'
// crossOrigin: 'anonymous'
// etc.
}
}
}
})
Add next,previous button
Events
type UIEventName = 'controlsshown' | 'controlshidden' | 'backward' | 'previous' | 'next'