build all

Refactor utility functions for improved readability and consistency

- Updated compatibility.js to use consistent line endings and formatting.
- Refactored component.js for better readability and structure, including consistent use of arrow functions and binding.
- Simplified dom.js functions with consistent formatting and improved readability.
- Enhanced emitter.js with consistent formatting and arrow function usage.
- Streamlined error.js for improved readability and consistency.
- Refactored file.js to use consistent formatting and improved readability.
- Improved format.js functions for better readability and consistency.
- Updated index.js to maintain consistent export formatting.
- Refactored property.js for improved readability and consistency.
- Enhanced subtitle.js with consistent formatting and improved readability.
- Refactored time.js for better readability and consistent formatting.

fix: update lint script to target only source files for consistency

Revert "fix: restore export statements for various plugins and update type definitions for better clarity"

This reverts commit b5e969d81ec765cd8a34b06e6ef4f232ef25087f.

fix: restore export statements for various plugins and update type definitions for better clarity

Refactor type definitions to use interfaces instead of type aliases for better extensibility and clarity across i18n, icons, option, player, quality, setting, subtitle, template, and utils modules.
This commit is contained in:
Harvey Zack committed 2025-08-03 13:17:33 +08:00
1 parent 532dd67cfd
commit 35ad9883af
447 files changed
+18248 -17946

No files matched your search

+80 -80
View File
@@ -1,81 +1,81 @@
export default {
properties: [
'audioTracks',
'autoplay',
'buffered',
'controller',
'controls',
'crossOrigin',
'currentSrc',
'currentTime',
'defaultMuted',
'defaultPlaybackRate',
'duration',
'ended',
'error',
'loop',
'mediaGroup',
'muted',
'networkState',
'paused',
'playbackRate',
'played',
'preload',
'readyState',
'seekable',
'seeking',
'src',
'startDate',
'textTracks',
'videoTracks',
'volume',
],
methods: ['addTextTrack', 'canPlayType', 'load', 'play', 'pause'],
events: [
'abort',
'canplay',
'canplaythrough',
'durationchange',
'emptied',
'ended',
'error',
'loadeddata',
'loadedmetadata',
'loadstart',
'pause',
'play',
'playing',
'progress',
'ratechange',
'seeked',
'seeking',
'stalled',
'suspend',
'timeupdate',
'volumechange',
'waiting',
],
prototypes: [
'width',
'height',
'videoWidth',
'videoHeight',
'poster',
'webkitDecodedFrameCount',
'webkitDroppedFrameCount',
'playsInline',
'webkitSupportsFullscreen',
'webkitDisplayingFullscreen',
'onenterpictureinpicture',
'onleavepictureinpicture',
'disablePictureInPicture',
'cancelVideoFrameCallback',
'requestVideoFrameCallback',
'getVideoPlaybackQuality',
'requestPictureInPicture',
'webkitEnterFullScreen',
'webkitEnterFullscreen',
'webkitExitFullScreen',
'webkitExitFullscreen',
],
};
properties: [
'audioTracks',
'autoplay',
'buffered',
'controller',
'controls',
'crossOrigin',
'currentSrc',
'currentTime',
'defaultMuted',
'defaultPlaybackRate',
'duration',
'ended',
'error',
'loop',
'mediaGroup',
'muted',
'networkState',
'paused',
'playbackRate',
'played',
'preload',
'readyState',
'seekable',
'seeking',
'src',
'startDate',
'textTracks',
'videoTracks',
'volume',
],
methods: ['addTextTrack', 'canPlayType', 'load', 'play', 'pause'],
events: [
'abort',
'canplay',
'canplaythrough',
'durationchange',
'emptied',
'ended',
'error',
'loadeddata',
'loadedmetadata',
'loadstart',
'pause',
'play',
'playing',
'progress',
'ratechange',
'seeked',
'seeking',
'stalled',
'suspend',
'timeupdate',
'volumechange',
'waiting',
],
prototypes: [
'width',
'height',
'videoWidth',
'videoHeight',
'poster',
'webkitDecodedFrameCount',
'webkitDroppedFrameCount',
'playsInline',
'webkitSupportsFullscreen',
'webkitDisplayingFullscreen',
'onenterpictureinpicture',
'onleavepictureinpicture',
'disablePictureInPicture',
'cancelVideoFrameCallback',
'requestVideoFrameCallback',
'getVideoPlaybackQuality',
'requestPictureInPicture',
'webkitEnterFullScreen',
'webkitEnterFullscreen',
'webkitExitFullScreen',
'webkitExitFullscreen',
],
}
@@ -1,38 +1,38 @@
import { inverseClass, query, queryAll } from '../utils';
import { inverseClass, query, queryAll } from '../utils'
export default function aspectRatio(option) {
return (art) => {
const {
i18n,
constructor: { ASPECT_RATIO },
} = art;
return (art) => {
const {
i18n,
constructor: { ASPECT_RATIO },
} = art
const html = ASPECT_RATIO.map(
(item) => `<span data-value="${item}">${item === 'default' ? i18n.get('Default') : item}</span>`,
).join('');
const html = ASPECT_RATIO.map(
item => `<span data-value="${item}">${item === 'default' ? i18n.get('Default') : item}</span>`,
).join('')
return {
...option,
html: `${i18n.get('Aspect Ratio')}: ${html}`,
click: (contextmenu, event) => {
const { value } = event.target.dataset;
if (value) {
art.aspectRatio = value;
contextmenu.show = false;
}
},
mounted: ($panel) => {
const $default = query('[data-value="default"]', $panel);
if ($default) {
inverseClass($default, 'art-current');
}
art.on('aspectRatio', (value) => {
const $current = queryAll('span', $panel).find((item) => item.dataset.value === value);
if ($current) {
inverseClass($current, 'art-current');
}
});
},
};
};
return {
...option,
html: `${i18n.get('Aspect Ratio')}: ${html}`,
click: (contextmenu, event) => {
const { value } = event.target.dataset
if (value) {
art.aspectRatio = value
contextmenu.show = false
}
},
mounted: ($panel) => {
const $default = query('[data-value="default"]', $panel)
if ($default) {
inverseClass($default, 'art-current')
}
art.on('aspectRatio', (value) => {
const $current = queryAll('span', $panel).find(item => item.dataset.value === value)
if ($current) {
inverseClass($current, 'art-current')
}
})
},
}
}
}
+7 -7
View File
@@ -1,9 +1,9 @@
export default function close(option) {
return (art) => ({
...option,
html: art.i18n.get('Close'),
click: (contextmenu) => {
contextmenu.show = false;
},
});
return art => ({
...option,
html: art.i18n.get('Close'),
click: (contextmenu) => {
contextmenu.show = false
},
})
}
+31 -31
View File
@@ -1,36 +1,36 @@
import { inverseClass, query, queryAll, capitalize } from '../utils';
import { capitalize, inverseClass, query, queryAll } from '../utils'
export default function flip(option) {
return (art) => {
const {
i18n,
constructor: { FLIP },
} = art;
return (art) => {
const {
i18n,
constructor: { FLIP },
} = art
const html = FLIP.map((item) => `<span data-value="${item}">${i18n.get(capitalize(item))}</span>`).join('');
const html = FLIP.map(item => `<span data-value="${item}">${i18n.get(capitalize(item))}</span>`).join('')
return {
...option,
html: `${i18n.get('Video Flip')}: ${html}`,
click: (contextmenu, event) => {
const { value } = event.target.dataset;
if (value) {
art.flip = value.toLowerCase();
contextmenu.show = false;
}
},
mounted: ($panel) => {
const $default = query('[data-value="normal"]', $panel);
if ($default) {
inverseClass($default, 'art-current');
}
art.on('flip', (value) => {
const $current = queryAll('span', $panel).find((item) => item.dataset.value === value);
if ($current) {
inverseClass($current, 'art-current');
}
});
},
};
};
return {
...option,
html: `${i18n.get('Video Flip')}: ${html}`,
click: (contextmenu, event) => {
const { value } = event.target.dataset
if (value) {
art.flip = value.toLowerCase()
contextmenu.show = false
}
},
mounted: ($panel) => {
const $default = query('[data-value="normal"]', $panel)
if ($default) {
inverseClass($default, 'art-current')
}
art.on('flip', (value) => {
const $current = queryAll('span', $panel).find(item => item.dataset.value === value)
if ($current) {
inverseClass($current, 'art-current')
}
})
},
}
}
}
+111 -110
View File
@@ -1,118 +1,119 @@
import { setStyles, includeFromEvent, isMobile, getRect } from '../utils';
import Component from '../utils/component';
import playbackRate from './playbackRate';
import aspectRatio from './aspectRatio';
import flip from './flip';
import info from './info';
import version from './version';
import close from './close';
import { getRect, includeFromEvent, isMobile, setStyles } from '../utils'
import Component from '../utils/component'
import aspectRatio from './aspectRatio'
import close from './close'
import flip from './flip'
import info from './info'
import playbackRate from './playbackRate'
import version from './version'
export default class Contextmenu extends Component {
constructor(art) {
super(art);
constructor(art) {
super(art)
this.name = 'contextmenu';
this.$parent = art.template.$contextmenu;
this.name = 'contextmenu'
this.$parent = art.template.$contextmenu
if (!isMobile) {
this.init();
}
if (!isMobile) {
this.init()
}
}
init() {
const {
option,
proxy,
template: { $player, $contextmenu },
} = this.art
if (option.playbackRate) {
this.add(
playbackRate({
name: 'playbackRate',
index: 10,
}),
)
}
init() {
const {
option,
proxy,
template: { $player, $contextmenu },
} = this.art;
if (option.playbackRate) {
this.add(
playbackRate({
name: 'playbackRate',
index: 10,
}),
);
}
if (option.aspectRatio) {
this.add(
aspectRatio({
name: 'aspectRatio',
index: 20,
}),
);
}
if (option.flip) {
this.add(
flip({
name: 'flip',
index: 30,
}),
);
}
this.add(
info({
name: 'info',
index: 40,
}),
);
this.add(
version({
name: 'version',
index: 50,
}),
);
this.add(
close({
name: 'close',
index: 60,
}),
);
for (let index = 0; index < option.contextmenu.length; index++) {
this.add(option.contextmenu[index]);
}
proxy($player, 'contextmenu', (event) => {
if (!this.art.constructor.CONTEXTMENU) return;
event.preventDefault();
this.show = true;
const mouseX = event.clientX;
const mouseY = event.clientY;
const { height: cHeight, width: cWidth, left: cLeft, top: cTop } = getRect($player);
const { height: mHeight, width: mWidth } = getRect($contextmenu);
let menuLeft = mouseX - cLeft;
let menuTop = mouseY - cTop;
if (mouseX + mWidth > cLeft + cWidth) {
menuLeft = cWidth - mWidth;
}
if (mouseY + mHeight > cTop + cHeight) {
menuTop = cHeight - mHeight;
}
setStyles($contextmenu, {
top: `${menuTop}px`,
left: `${menuLeft}px`,
});
});
proxy($player, 'click', (event) => {
if (!includeFromEvent(event, $contextmenu)) {
this.show = false;
}
});
this.art.on('blur', () => {
this.show = false;
});
if (option.aspectRatio) {
this.add(
aspectRatio({
name: 'aspectRatio',
index: 20,
}),
)
}
if (option.flip) {
this.add(
flip({
name: 'flip',
index: 30,
}),
)
}
this.add(
info({
name: 'info',
index: 40,
}),
)
this.add(
version({
name: 'version',
index: 50,
}),
)
this.add(
close({
name: 'close',
index: 60,
}),
)
for (let index = 0; index < option.contextmenu.length; index++) {
this.add(option.contextmenu[index])
}
proxy($player, 'contextmenu', (event) => {
if (!this.art.constructor.CONTEXTMENU)
return
event.preventDefault()
this.show = true
const mouseX = event.clientX
const mouseY = event.clientY
const { height: cHeight, width: cWidth, left: cLeft, top: cTop } = getRect($player)
const { height: mHeight, width: mWidth } = getRect($contextmenu)
let menuLeft = mouseX - cLeft
let menuTop = mouseY - cTop
if (mouseX + mWidth > cLeft + cWidth) {
menuLeft = cWidth - mWidth
}
if (mouseY + mHeight > cTop + cHeight) {
menuTop = cHeight - mHeight
}
setStyles($contextmenu, {
top: `${menuTop}px`,
left: `${menuLeft}px`,
})
})
proxy($player, 'click', (event) => {
if (!includeFromEvent(event, $contextmenu)) {
this.show = false
}
})
this.art.on('blur', () => {
this.show = false
})
}
}
+8 -8
View File
@@ -1,10 +1,10 @@
export default function info(option) {
return (art) => ({
...option,
html: art.i18n.get('Video Info'),
click: (contextmenu) => {
art.info.show = true;
contextmenu.show = false;
},
});
return art => ({
...option,
html: art.i18n.get('Video Info'),
click: (contextmenu) => {
art.info.show = true
contextmenu.show = false
},
})
}
@@ -1,38 +1,39 @@
import { inverseClass, query, queryAll } from '../utils';
import { inverseClass, query, queryAll } from '../utils'
export default function playbackRate(option) {
return (art) => {
const {
i18n,
constructor: { PLAYBACK_RATE },
} = art;
return (art) => {
const {
i18n,
constructor: { PLAYBACK_RATE },
} = art
const html = PLAYBACK_RATE.map(
(item) => `<span data-value="${item}">${item === 1 ? i18n.get('Normal') : item.toFixed(1)}</span>`,
).join('');
const html = PLAYBACK_RATE.map(
item => `<span data-value="${item}">${item === 1 ? i18n.get('Normal') : item.toFixed(1)}</span>`,
).join('')
return {
...option,
html: `${i18n.get('Play Speed')}: ${html}`,
click: (contextmenu, event) => {
const { value } = event.target.dataset;
if (value) {
art.playbackRate = Number(value);
contextmenu.show = false;
}
},
mounted: ($panel) => {
const $default = query('[data-value="1"]', $panel);
if ($default) inverseClass($default, 'art-current');
art.on('video:ratechange', () => {
const $current = queryAll('span', $panel).find(
(item) => Number(item.dataset.value) === art.playbackRate,
);
if ($current) {
inverseClass($current, 'art-current');
}
});
},
};
};
return {
...option,
html: `${i18n.get('Play Speed')}: ${html}`,
click: (contextmenu, event) => {
const { value } = event.target.dataset
if (value) {
art.playbackRate = Number(value)
contextmenu.show = false
}
},
mounted: ($panel) => {
const $default = query('[data-value="1"]', $panel)
if ($default)
inverseClass($default, 'art-current')
art.on('video:ratechange', () => {
const $current = queryAll('span', $panel).find(
item => Number(item.dataset.value) === art.playbackRate,
)
if ($current) {
inverseClass($current, 'art-current')
}
})
},
}
}
}
@@ -1,8 +1,8 @@
import { version } from '../../package.json';
import { version } from '../../package.json'
export default function (option) {
return {
...option,
html: `<a href="https://artplayer.org" target="_blank">ArtPlayer ${version}</a>`,
};
return {
...option,
html: `<a href="https://artplayer.org" target="_blank">ArtPlayer ${version}</a>`,
}
}
+10 -10
View File
@@ -1,13 +1,13 @@
import { append } from '../utils';
import { append } from '../utils'
export default function airplay(option) {
return (art) => ({
...option,
tooltip: art.i18n.get('AirPlay'),
mounted: ($control) => {
const { proxy, icons } = art;
append($control, icons.airplay);
proxy($control, 'click', () => art.airplay());
},
});
return art => ({
...option,
tooltip: art.i18n.get('AirPlay'),
mounted: ($control) => {
const { proxy, icons } = art
append($control, icons.airplay)
proxy($control, 'click', () => art.airplay())
},
})
}
+26 -25
View File
@@ -1,31 +1,32 @@
import { append, tooltip, setStyle } from '../utils';
import { append, setStyle, tooltip } from '../utils'
export default function fullscreen(option) {
return (art) => ({
...option,
tooltip: art.i18n.get('Fullscreen'),
mounted: ($control) => {
const { proxy, icons, i18n } = art;
return art => ({
...option,
tooltip: art.i18n.get('Fullscreen'),
mounted: ($control) => {
const { proxy, icons, i18n } = art
const $fullscreenOn = append($control, icons.fullscreenOn);
const $fullscreenOff = append($control, icons.fullscreenOff);
setStyle($fullscreenOff, 'display', 'none');
const $fullscreenOn = append($control, icons.fullscreenOn)
const $fullscreenOff = append($control, icons.fullscreenOff)
setStyle($fullscreenOff, 'display', 'none')
proxy($control, 'click', () => {
art.fullscreen = !art.fullscreen;
});
proxy($control, 'click', () => {
art.fullscreen = !art.fullscreen
})
art.on('fullscreen', (state) => {
if (state) {
tooltip($control, i18n.get('Exit Fullscreen'));
setStyle($fullscreenOn, 'display', 'none');
setStyle($fullscreenOff, 'display', 'inline-flex');
} else {
tooltip($control, i18n.get('Fullscreen'));
setStyle($fullscreenOn, 'display', 'inline-flex');
setStyle($fullscreenOff, 'display', 'none');
}
});
},
});
art.on('fullscreen', (state) => {
if (state) {
tooltip($control, i18n.get('Exit Fullscreen'))
setStyle($fullscreenOn, 'display', 'none')
setStyle($fullscreenOff, 'display', 'inline-flex')
}
else {
tooltip($control, i18n.get('Fullscreen'))
setStyle($fullscreenOn, 'display', 'inline-flex')
setStyle($fullscreenOff, 'display', 'none')
}
})
},
})
}
+26 -25
View File
@@ -1,31 +1,32 @@
import { append, tooltip, setStyle } from '../utils';
import { append, setStyle, tooltip } from '../utils'
export default function fullscreenWeb(option) {
return (art) => ({
...option,
tooltip: art.i18n.get('Web Fullscreen'),
mounted: ($control) => {
const { proxy, icons, i18n } = art;
return art => ({
...option,
tooltip: art.i18n.get('Web Fullscreen'),
mounted: ($control) => {
const { proxy, icons, i18n } = art
const $fullscreenWebOn = append($control, icons.fullscreenWebOn);
const $fullscreenWebOff = append($control, icons.fullscreenWebOff);
setStyle($fullscreenWebOff, 'display', 'none');
const $fullscreenWebOn = append($control, icons.fullscreenWebOn)
const $fullscreenWebOff = append($control, icons.fullscreenWebOff)
setStyle($fullscreenWebOff, 'display', 'none')
proxy($control, 'click', () => {
art.fullscreenWeb = !art.fullscreenWeb;
});
proxy($control, 'click', () => {
art.fullscreenWeb = !art.fullscreenWeb
})
art.on('fullscreenWeb', (value) => {
if (value) {
tooltip($control, i18n.get('Exit Web Fullscreen'));
setStyle($fullscreenWebOn, 'display', 'none');
setStyle($fullscreenWebOff, 'display', 'inline-flex');
} else {
tooltip($control, i18n.get('Web Fullscreen'));
setStyle($fullscreenWebOn, 'display', 'inline-flex');
setStyle($fullscreenWebOff, 'display', 'none');
}
});
},
});
art.on('fullscreenWeb', (value) => {
if (value) {
tooltip($control, i18n.get('Exit Web Fullscreen'))
setStyle($fullscreenWebOn, 'display', 'none')
setStyle($fullscreenWebOff, 'display', 'inline-flex')
}
else {
tooltip($control, i18n.get('Web Fullscreen'))
setStyle($fullscreenWebOn, 'display', 'inline-flex')
setStyle($fullscreenWebOff, 'display', 'none')
}
})
},
})
}
+260 -257
View File
@@ -1,281 +1,284 @@
import Component from '../utils/component';
import fullscreen from './fullscreen';
import fullscreenWeb from './fullscreenWeb';
import pip from './pip';
import playAndPause from './playAndPause';
import progress from './progress';
import time from './time';
import volume from './volume';
import setting from './setting';
import screenshot from './screenshot';
import airplay from './airplay';
import {
def,
sleep,
append,
addClass,
isMobile,
removeClass,
errorHandle,
inverseClass,
createElement,
includeFromEvent,
getComposedPath,
} from '../utils';
addClass,
append,
createElement,
def,
errorHandle,
getComposedPath,
includeFromEvent,
inverseClass,
isMobile,
removeClass,
sleep,
} from '../utils'
import Component from '../utils/component'
import airplay from './airplay'
import fullscreen from './fullscreen'
import fullscreenWeb from './fullscreenWeb'
import pip from './pip'
import playAndPause from './playAndPause'
import progress from './progress'
import screenshot from './screenshot'
import setting from './setting'
import time from './time'
import volume from './volume'
export default class Control extends Component {
constructor(art) {
super(art);
constructor(art) {
super(art)
this.isHover = false;
this.name = 'control';
this.timer = Date.now();
this.isHover = false
this.name = 'control'
this.timer = Date.now()
const { constructor } = art;
const { $player, $bottom } = this.art.template;
const { constructor } = art
const { $player, $bottom } = this.art.template
art.on('mousemove', () => {
if (!isMobile) {
this.show = true;
}
});
art.on('mousemove', () => {
if (!isMobile) {
this.show = true
}
})
art.on('click', () => {
if (isMobile) {
this.toggle();
} else {
this.show = true;
}
});
art.on('click', () => {
if (isMobile) {
this.toggle()
}
else {
this.show = true
}
})
art.on('document:mousemove', (event) => {
this.isHover = includeFromEvent(event, $bottom);
});
art.on('document:mousemove', (event) => {
this.isHover = includeFromEvent(event, $bottom)
})
art.on('video:timeupdate', () => {
if (
!art.setting.show &&
!this.isHover &&
!art.isInput &&
art.playing &&
this.show &&
Date.now() - this.timer >= constructor.CONTROL_HIDE_TIME
) {
this.show = false;
}
});
art.on('video:timeupdate', () => {
if (
!art.setting.show
&& !this.isHover
&& !art.isInput
&& art.playing
&& this.show
&& Date.now() - this.timer >= constructor.CONTROL_HIDE_TIME
) {
this.show = false
}
})
art.on('control', (state) => {
if (state) {
removeClass($player, 'art-hide-cursor');
addClass($player, 'art-hover');
this.timer = Date.now();
} else {
addClass($player, 'art-hide-cursor');
removeClass($player, 'art-hover');
}
});
art.on('control', (state) => {
if (state) {
removeClass($player, 'art-hide-cursor')
addClass($player, 'art-hover')
this.timer = Date.now()
}
else {
addClass($player, 'art-hide-cursor')
removeClass($player, 'art-hover')
}
})
this.init();
this.init()
}
init() {
const { option } = this.art
if (!option.isLive) {
this.add(
progress({
name: 'progress',
position: 'top',
index: 10,
}),
)
}
init() {
const { option } = this.art;
this.add({
name: 'thumbnails',
position: 'top',
index: 20,
})
if (!option.isLive) {
this.add(
progress({
name: 'progress',
position: 'top',
index: 10,
}),
);
}
this.add(
playAndPause({
name: 'playAndPause',
position: 'left',
index: 10,
}),
)
this.add({
name: 'thumbnails',
position: 'top',
index: 20,
});
this.add(
volume({
name: 'volume',
position: 'left',
index: 20,
}),
)
this.add(
playAndPause({
name: 'playAndPause',
position: 'left',
index: 10,
}),
);
this.add(
volume({
name: 'volume',
position: 'left',
index: 20,
}),
);
if (!option.isLive) {
this.add(
time({
name: 'time',
position: 'left',
index: 30,
}),
);
}
if (option.quality.length) {
sleep().then(() => {
this.art.quality = option.quality;
});
}
if (option.screenshot && !isMobile) {
this.add(
screenshot({
name: 'screenshot',
position: 'right',
index: 20,
}),
);
}
if (option.setting) {
this.add(
setting({
name: 'setting',
position: 'right',
index: 30,
}),
);
}
if (option.pip) {
this.add(
pip({
name: 'pip',
position: 'right',
index: 40,
}),
);
}
if (option.airplay && window.WebKitPlaybackTargetAvailabilityEvent) {
this.add(
airplay({
name: 'airplay',
position: 'right',
index: 50,
}),
);
}
if (option.fullscreenWeb) {
this.add(
fullscreenWeb({
name: 'fullscreenWeb',
position: 'right',
index: 60,
}),
);
}
if (option.fullscreen) {
this.add(
fullscreen({
name: 'fullscreen',
position: 'right',
index: 70,
}),
);
}
for (let index = 0; index < option.controls.length; index++) {
this.add(option.controls[index]);
}
if (!option.isLive) {
this.add(
time({
name: 'time',
position: 'left',
index: 30,
}),
)
}
add(getOption) {
const option = typeof getOption === 'function' ? getOption(this.art) : getOption;
const { $progress, $controlsLeft, $controlsRight } = this.art.template;
switch (option.position) {
case 'top':
this.$parent = $progress;
break;
case 'left':
this.$parent = $controlsLeft;
break;
case 'right':
this.$parent = $controlsRight;
break;
default:
errorHandle(false, `Control option.position must one of 'top', 'left', 'right'`);
break;
}
super.add(option);
if (option.quality.length) {
sleep().then(() => {
this.art.quality = option.quality
})
}
check(target) {
target.$control_value.innerHTML = target.html;
for (let index = 0; index < target.$control_option.length; index++) {
const item = target.$control_option[index];
item.default = item === target;
if (item.default) {
inverseClass(item.$control_item, 'art-current');
}
}
if (option.screenshot && !isMobile) {
this.add(
screenshot({
name: 'screenshot',
position: 'right',
index: 20,
}),
)
}
selector(option, $ref, events) {
const { proxy } = this.art.events;
addClass($ref, 'art-control-selector');
const $value = createElement('div');
addClass($value, 'art-selector-value');
append($value, option.html);
$ref.innerText = '';
append($ref, $value);
const $list = createElement('div');
addClass($list, 'art-selector-list');
append($ref, $list);
for (let index = 0; index < option.selector.length; index++) {
const item = option.selector[index];
const $item = createElement('div');
addClass($item, 'art-selector-item');
if (item.default) addClass($item, 'art-current');
$item.dataset.index = index;
$item.dataset.value = item.value;
$item.innerHTML = item.html;
append($list, $item);
def(item, '$control_option', {
get: () => option.selector,
});
def(item, '$control_item', {
get: () => $item,
});
def(item, '$control_value', {
get: () => $value,
});
}
const event = proxy($list, 'click', async (event) => {
const path = getComposedPath(event);
const item = option.selector.find(
(item) => item.$control_item === path.find(($item) => item.$control_item === $item),
);
this.check(item);
if (option.onSelect) {
$value.innerHTML = await option.onSelect.call(this.art, item, item.$control_item, event);
}
});
events.push(event);
if (option.setting) {
this.add(
setting({
name: 'setting',
position: 'right',
index: 30,
}),
)
}
if (option.pip) {
this.add(
pip({
name: 'pip',
position: 'right',
index: 40,
}),
)
}
if (option.airplay && window.WebKitPlaybackTargetAvailabilityEvent) {
this.add(
airplay({
name: 'airplay',
position: 'right',
index: 50,
}),
)
}
if (option.fullscreenWeb) {
this.add(
fullscreenWeb({
name: 'fullscreenWeb',
position: 'right',
index: 60,
}),
)
}
if (option.fullscreen) {
this.add(
fullscreen({
name: 'fullscreen',
position: 'right',
index: 70,
}),
)
}
for (let index = 0; index < option.controls.length; index++) {
this.add(option.controls[index])
}
}
add(getOption) {
const option = typeof getOption === 'function' ? getOption(this.art) : getOption
const { $progress, $controlsLeft, $controlsRight } = this.art.template
switch (option.position) {
case 'top':
this.$parent = $progress
break
case 'left':
this.$parent = $controlsLeft
break
case 'right':
this.$parent = $controlsRight
break
default:
errorHandle(false, `Control option.position must one of 'top', 'left', 'right'`)
break
}
super.add(option)
}
check(target) {
target.$control_value.innerHTML = target.html
for (let index = 0; index < target.$control_option.length; index++) {
const item = target.$control_option[index]
item.default = item === target
if (item.default) {
inverseClass(item.$control_item, 'art-current')
}
}
}
selector(option, $ref, events) {
const { proxy } = this.art.events
addClass($ref, 'art-control-selector')
const $value = createElement('div')
addClass($value, 'art-selector-value')
append($value, option.html)
$ref.textContent = ''
append($ref, $value)
const $list = createElement('div')
addClass($list, 'art-selector-list')
append($ref, $list)
for (let index = 0; index < option.selector.length; index++) {
const item = option.selector[index]
const $item = createElement('div')
addClass($item, 'art-selector-item')
if (item.default)
addClass($item, 'art-current')
$item.dataset.index = index
$item.dataset.value = item.value
$item.innerHTML = item.html
append($list, $item)
def(item, '$control_option', {
get: () => option.selector,
})
def(item, '$control_item', {
get: () => $item,
})
def(item, '$control_value', {
get: () => $value,
})
}
const event = proxy($list, 'click', async (event) => {
const path = getComposedPath(event)
const item = option.selector.find(
item => item.$control_item === path.find($item => item.$control_item === $item),
)
this.check(item)
if (option.onSelect) {
$value.innerHTML = await option.onSelect.call(this.art, item, item.$control_item, event)
}
})
events.push(event)
}
}
+15 -15
View File
@@ -1,21 +1,21 @@
import { append, tooltip } from '../utils';
import { append, tooltip } from '../utils'
export default function pip(option) {
return (art) => ({
...option,
tooltip: art.i18n.get('PIP Mode'),
mounted: ($control) => {
const { proxy, icons, i18n } = art;
return art => ({
...option,
tooltip: art.i18n.get('PIP Mode'),
mounted: ($control) => {
const { proxy, icons, i18n } = art
append($control, icons.pip);
append($control, icons.pip)
proxy($control, 'click', () => {
art.pip = !art.pip;
});
proxy($control, 'click', () => {
art.pip = !art.pip
})
art.on('pip', (value) => {
tooltip($control, i18n.get(value ? 'Exit PIP Mode' : 'PIP Mode'));
});
},
});
art.on('pip', (value) => {
tooltip($control, i18n.get(value ? 'Exit PIP Mode' : 'PIP Mode'))
})
},
})
}
+37 -36
View File
@@ -1,47 +1,48 @@
import { append, tooltip, setStyle } from '../utils';
import { append, setStyle, tooltip } from '../utils'
export default function playAndPause(option) {
return (art) => ({
...option,
mounted: ($control) => {
const { proxy, icons, i18n } = art;
return art => ({
...option,
mounted: ($control) => {
const { proxy, icons, i18n } = art
const $play = append($control, icons.play);
const $pause = append($control, icons.pause);
tooltip($play, i18n.get('Play'));
tooltip($pause, i18n.get('Pause'));
const $play = append($control, icons.play)
const $pause = append($control, icons.pause)
tooltip($play, i18n.get('Play'))
tooltip($pause, i18n.get('Pause'))
proxy($play, 'click', () => {
art.play();
});
proxy($play, 'click', () => {
art.play()
})
proxy($pause, 'click', () => {
art.pause();
});
proxy($pause, 'click', () => {
art.pause()
})
function showPlay() {
setStyle($play, 'display', 'flex');
setStyle($pause, 'display', 'none');
}
function showPlay() {
setStyle($play, 'display', 'flex')
setStyle($pause, 'display', 'none')
}
function showPause() {
setStyle($play, 'display', 'none');
setStyle($pause, 'display', 'flex');
}
function showPause() {
setStyle($play, 'display', 'none')
setStyle($pause, 'display', 'flex')
}
if (art.playing) {
showPause();
} else {
showPlay();
}
if (art.playing) {
showPause()
}
else {
showPlay()
}
art.on('video:playing', () => {
showPause();
});
art.on('video:playing', () => {
showPause()
})
art.on('video:pause', () => {
showPlay();
});
},
});
art.on('video:pause', () => {
showPlay()
})
},
})
}
+161 -153
View File
@@ -1,36 +1,37 @@
import { query, clamp, append, setStyle, secondToTime, includeFromEvent, isMobile, getRect } from '../utils';
import { append, clamp, getRect, includeFromEvent, isMobile, query, secondToTime, setStyle } from '../utils'
export function getPosFromEvent(art, event) {
const { $progress } = art.template;
const { left } = getRect($progress);
const eventLeft = isMobile ? event.touches[0].clientX : event.clientX;
const width = clamp(eventLeft - left, 0, $progress.clientWidth);
const second = (width / $progress.clientWidth) * art.duration;
const time = secondToTime(second);
const percentage = clamp(width / $progress.clientWidth, 0, 1);
return { second, time, width, percentage };
const { $progress } = art.template
const { left } = getRect($progress)
const eventLeft = isMobile ? event.touches[0].clientX : event.clientX
const width = clamp(eventLeft - left, 0, $progress.clientWidth)
const second = (width / $progress.clientWidth) * art.duration
const time = secondToTime(second)
const percentage = clamp(width / $progress.clientWidth, 0, 1)
return { second, time, width, percentage }
}
export function setCurrentTime(art, event) {
if (art.isRotate) {
const percentage = event.touches[0].clientY / art.height;
const second = percentage * art.duration;
art.emit('setBar', 'played', percentage, event);
art.seek = second;
} else {
const { second, percentage } = getPosFromEvent(art, event);
art.emit('setBar', 'played', percentage, event);
art.seek = second;
}
if (art.isRotate) {
const percentage = event.touches[0].clientY / art.height
const second = percentage * art.duration
art.emit('setBar', 'played', percentage, event)
art.seek = second
}
else {
const { second, percentage } = getPosFromEvent(art, event)
art.emit('setBar', 'played', percentage, event)
art.seek = second
}
}
export default function progress(options) {
return (art) => {
const { icons, option, proxy } = art;
return (art) => {
const { icons, option, proxy } = art
return {
...options,
html: `
return {
...options,
html: `
<div class="art-control-progress-inner">
<div class="art-progress-hover"></div>
<div class="art-progress-loaded"></div>
@@ -40,154 +41,161 @@ export default function progress(options) {
<div class="art-progress-tip"></div>
</div>
`,
mounted: ($control) => {
let tipTimer = null;
let isDragging = false;
mounted: ($control) => {
let tipTimer = null
let isDragging = false
const $hover = query('.art-progress-hover', $control);
const $loaded = query('.art-progress-loaded', $control);
const $played = query('.art-progress-played', $control);
const $highlight = query('.art-progress-highlight', $control);
const $indicator = query('.art-progress-indicator', $control);
const $tip = query('.art-progress-tip', $control);
const $hover = query('.art-progress-hover', $control)
const $loaded = query('.art-progress-loaded', $control)
const $played = query('.art-progress-played', $control)
const $highlight = query('.art-progress-highlight', $control)
const $indicator = query('.art-progress-indicator', $control)
const $tip = query('.art-progress-tip', $control)
if (icons.indicator) {
append($indicator, icons.indicator);
} else {
setStyle($indicator, 'backgroundColor', 'var(--art-theme)');
}
if (icons.indicator) {
append($indicator, icons.indicator)
}
else {
setStyle($indicator, 'backgroundColor', 'var(--art-theme)')
}
function showHighlight(event) {
const { width } = getPosFromEvent(art, event);
const { text } = event.target.dataset;
$tip.innerText = text;
const tipWidth = $tip.clientWidth;
if (width <= tipWidth / 2) {
setStyle($tip, 'left', 0);
} else if (width > $control.clientWidth - tipWidth / 2) {
setStyle($tip, 'left', `${$control.clientWidth - tipWidth}px`);
} else {
setStyle($tip, 'left', `${width - tipWidth / 2}px`);
}
}
function showHighlight(event) {
const { width } = getPosFromEvent(art, event)
const { text } = event.target.dataset
$tip.textContent = text
const tipWidth = $tip.clientWidth
if (width <= tipWidth / 2) {
setStyle($tip, 'left', 0)
}
else if (width > $control.clientWidth - tipWidth / 2) {
setStyle($tip, 'left', `${$control.clientWidth - tipWidth}px`)
}
else {
setStyle($tip, 'left', `${width - tipWidth / 2}px`)
}
}
function showTime(event, touch) {
const { width, time } = touch || getPosFromEvent(art, event);
$tip.innerText = time;
const tipWidth = $tip.clientWidth;
if (width <= tipWidth / 2) {
setStyle($tip, 'left', 0);
} else if (width > $control.clientWidth - tipWidth / 2) {
setStyle($tip, 'left', `${$control.clientWidth - tipWidth}px`);
} else {
setStyle($tip, 'left', `${width - tipWidth / 2}px`);
}
}
function showTime(event, touch) {
const { width, time } = touch || getPosFromEvent(art, event)
$tip.textContent = time
const tipWidth = $tip.clientWidth
if (width <= tipWidth / 2) {
setStyle($tip, 'left', 0)
}
else if (width > $control.clientWidth - tipWidth / 2) {
setStyle($tip, 'left', `${$control.clientWidth - tipWidth}px`)
}
else {
setStyle($tip, 'left', `${width - tipWidth / 2}px`)
}
}
function updateHighlight() {
$highlight.innerText = '';
for (let index = 0; index < option.highlight.length; index++) {
const item = option.highlight[index];
const left = (clamp(item.time, 0, art.duration) / art.duration) * 100;
const html = `<span data-text="${item.text}" data-time="${item.time}" style="left: ${left}%"></span>`;
append($highlight, html);
}
}
function updateHighlight() {
$highlight.textContent = ''
for (let index = 0; index < option.highlight.length; index++) {
const item = option.highlight[index]
const left = (clamp(item.time, 0, art.duration) / art.duration) * 100
const html = `<span data-text="${item.text}" data-time="${item.time}" style="left: ${left}%"></span>`
append($highlight, html)
}
}
function setBar(type, percentage, event) {
const isMobileDragging = type === 'played' && event && isMobile;
function setBar(type, percentage, event) {
const isMobileDragging = type === 'played' && event && isMobile
if (type === 'loaded') {
setStyle($loaded, 'width', `${percentage * 100}%`);
}
if (type === 'loaded') {
setStyle($loaded, 'width', `${percentage * 100}%`)
}
if (type === 'hover') {
setStyle($hover, 'width', `${percentage * 100}%`);
}
if (type === 'hover') {
setStyle($hover, 'width', `${percentage * 100}%`)
}
if (type === 'played') {
setStyle($played, 'width', `${percentage * 100}%`);
setStyle($indicator, 'left', `${percentage * 100}%`);
}
if (type === 'played') {
setStyle($played, 'width', `${percentage * 100}%`)
setStyle($indicator, 'left', `${percentage * 100}%`)
}
if (isMobileDragging) {
setStyle($tip, 'display', 'flex');
const width = $control.clientWidth * percentage;
const time = secondToTime(percentage * art.duration);
showTime(event, { width, time });
clearTimeout(tipTimer);
tipTimer = setTimeout(() => {
setStyle($tip, 'display', 'none');
}, 500);
}
}
if (isMobileDragging) {
setStyle($tip, 'display', 'flex')
const width = $control.clientWidth * percentage
const time = secondToTime(percentage * art.duration)
showTime(event, { width, time })
clearTimeout(tipTimer)
tipTimer = setTimeout(() => {
setStyle($tip, 'display', 'none')
}, 500)
}
}
art.on('setBar', setBar);
art.on('video:loadedmetadata', updateHighlight);
art.on('setBar', setBar)
art.on('video:loadedmetadata', updateHighlight)
if (art.constructor.USE_RAF) {
art.on('raf', () => {
art.emit('setBar', 'played', art.played);
art.emit('setBar', 'loaded', art.loaded);
});
} else {
art.on('video:timeupdate', () => {
art.emit('setBar', 'played', art.played);
});
if (art.constructor.USE_RAF) {
art.on('raf', () => {
art.emit('setBar', 'played', art.played)
art.emit('setBar', 'loaded', art.loaded)
})
}
else {
art.on('video:timeupdate', () => {
art.emit('setBar', 'played', art.played)
})
art.on('video:progress', () => {
art.emit('setBar', 'loaded', art.loaded);
});
art.on('video:progress', () => {
art.emit('setBar', 'loaded', art.loaded)
})
art.on('video:ended', () => {
art.emit('setBar', 'played', 1);
});
}
art.on('video:ended', () => {
art.emit('setBar', 'played', 1)
})
}
art.emit('setBar', 'loaded', art.loaded || 0);
art.emit('setBar', 'loaded', art.loaded || 0)
if (!isMobile) {
proxy($control, 'click', (event) => {
if (event.target !== $indicator) {
setCurrentTime(art, event);
}
});
if (!isMobile) {
proxy($control, 'click', (event) => {
if (event.target !== $indicator) {
setCurrentTime(art, event)
}
})
proxy($control, 'mousemove', (event) => {
const { percentage } = getPosFromEvent(art, event);
art.emit('setBar', 'hover', percentage, event);
setStyle($tip, 'display', 'flex');
if (includeFromEvent(event, $highlight)) {
showHighlight(event);
} else {
showTime(event);
}
});
proxy($control, 'mousemove', (event) => {
const { percentage } = getPosFromEvent(art, event)
art.emit('setBar', 'hover', percentage, event)
setStyle($tip, 'display', 'flex')
if (includeFromEvent(event, $highlight)) {
showHighlight(event)
}
else {
showTime(event)
}
})
proxy($control, 'mouseleave', (event) => {
setStyle($tip, 'display', 'none');
art.emit('setBar', 'hover', 0, event);
});
proxy($control, 'mouseleave', (event) => {
setStyle($tip, 'display', 'none')
art.emit('setBar', 'hover', 0, event)
})
proxy($control, 'mousedown', (event) => {
isDragging = event.button === 0;
});
proxy($control, 'mousedown', (event) => {
isDragging = event.button === 0
})
art.on('document:mousemove', (event) => {
if (isDragging) {
const { second, percentage } = getPosFromEvent(art, event);
art.emit('setBar', 'played', percentage, event);
art.seek = second;
}
});
art.on('document:mousemove', (event) => {
if (isDragging) {
const { second, percentage } = getPosFromEvent(art, event)
art.emit('setBar', 'played', percentage, event)
art.seek = second
}
})
art.on('document:mouseup', () => {
if (isDragging) {
isDragging = false;
}
});
}
},
};
};
art.on('document:mouseup', () => {
if (isDragging) {
isDragging = false
}
})
}
},
}
}
}
+12 -12
View File
@@ -1,16 +1,16 @@
import { append } from '../utils';
import { append } from '../utils'
export default function screenshot(option) {
return (art) => ({
...option,
tooltip: art.i18n.get('Screenshot'),
mounted: ($control) => {
const { proxy, icons } = art;
return art => ({
...option,
tooltip: art.i18n.get('Screenshot'),
mounted: ($control) => {
const { proxy, icons } = art
append($control, icons.screenshot);
proxy($control, 'click', () => {
art.screenshot();
});
},
});
append($control, icons.screenshot)
proxy($control, 'click', () => {
art.screenshot()
})
},
})
}
+16 -16
View File
@@ -1,22 +1,22 @@
import { append, tooltip } from '../utils';
import { append, tooltip } from '../utils'
export default function setting(option) {
return (art) => ({
...option,
tooltip: art.i18n.get('Show Setting'),
mounted: ($control) => {
const { proxy, icons, i18n } = art;
return art => ({
...option,
tooltip: art.i18n.get('Show Setting'),
mounted: ($control) => {
const { proxy, icons, i18n } = art
append($control, icons.setting);
append($control, icons.setting)
proxy($control, 'click', () => {
art.setting.toggle();
art.setting.resize();
});
proxy($control, 'click', () => {
art.setting.toggle()
art.setting.resize()
})
art.on('setting', (value) => {
tooltip($control, i18n.get(value ? 'Hide Setting' : 'Show Setting'));
});
},
});
art.on('setting', (value) => {
tooltip($control, i18n.get(value ? 'Hide Setting' : 'Show Setting'))
})
},
})
}
+27 -27
View File
@@ -1,31 +1,31 @@
import { secondToTime, isMobile } from '../utils';
import { isMobile, secondToTime } from '../utils'
export default function time(option) {
return (art) => ({
...option,
style: isMobile
? {
fontSize: '12px',
padding: '0 5px',
}
: {
cursor: 'auto',
padding: '0 10px',
},
mounted: ($control) => {
function getTime() {
const newTime = `${secondToTime(art.currentTime)} / ${secondToTime(art.duration)}`;
if (newTime !== $control.innerText) {
$control.innerText = newTime;
}
}
getTime();
const events = ['video:loadedmetadata', 'video:timeupdate', 'video:progress'];
for (let index = 0; index < events.length; index++) {
art.on(events[index], getTime);
}
return art => ({
...option,
style: isMobile
? {
fontSize: '12px',
padding: '0 5px',
}
: {
cursor: 'auto',
padding: '0 10px',
},
});
mounted: ($control) => {
function getTime() {
const newTime = `${secondToTime(art.currentTime)} / ${secondToTime(art.duration)}`
if (newTime !== $control.textContent) {
$control.textContent = newTime
}
}
getTime()
const events = ['video:loadedmetadata', 'video:timeupdate', 'video:progress']
for (let index = 0; index < events.length; index++) {
art.on(events[index], getTime)
}
},
})
}
+66 -64
View File
@@ -1,77 +1,79 @@
import { append, setStyle, isMobile, getRect } from '../utils';
import { append, getRect, isMobile, setStyle } from '../utils'
export default function volume(option) {
return (art) => ({
...option,
mounted: ($control) => {
const { proxy, icons } = art;
return art => ({
...option,
mounted: ($control) => {
const { proxy, icons } = art
const $volume = append($control, icons.volume);
const $close = append($control, icons.volumeClose);
const $panel = append($control, '<div class="art-volume-panel"></div>');
const $inner = append($panel, '<div class="art-volume-inner"></div>');
const $value = append($inner, `<div class="art-volume-val"></div>`);
const $slider = append($inner, `<div class="art-volume-slider"></div>`);
const $handle = append($slider, `<div class="art-volume-handle"></div>`);
const $loaded = append($handle, `<div class="art-volume-loaded"></div>`);
const $indicator = append($slider, `<div class="art-volume-indicator"></div>`);
const $volume = append($control, icons.volume)
const $close = append($control, icons.volumeClose)
const $panel = append($control, '<div class="art-volume-panel"></div>')
const $inner = append($panel, '<div class="art-volume-inner"></div>')
const $value = append($inner, `<div class="art-volume-val"></div>`)
const $slider = append($inner, `<div class="art-volume-slider"></div>`)
const $handle = append($slider, `<div class="art-volume-handle"></div>`)
const $loaded = append($handle, `<div class="art-volume-loaded"></div>`)
const $indicator = append($slider, `<div class="art-volume-indicator"></div>`)
function getVolumeFromEvent(event) {
const { top, height } = getRect($slider);
return 1 - (event.clientY - top) / height;
}
function getVolumeFromEvent(event) {
const { top, height } = getRect($slider)
return 1 - (event.clientY - top) / height
}
function update() {
if (art.muted || art.volume === 0) {
setStyle($volume, 'display', 'none');
setStyle($close, 'display', 'flex');
setStyle($indicator, 'top', '100%');
setStyle($loaded, 'top', '100%');
$value.innerText = 0;
} else {
const percentage = art.volume * 100;
setStyle($volume, 'display', 'flex');
setStyle($close, 'display', 'none');
setStyle($indicator, 'top', `${100 - percentage}%`);
setStyle($loaded, 'top', `${100 - percentage}%`);
$value.innerText = Math.floor(percentage);
}
}
function update() {
if (art.muted || art.volume === 0) {
setStyle($volume, 'display', 'none')
setStyle($close, 'display', 'flex')
setStyle($indicator, 'top', '100%')
setStyle($loaded, 'top', '100%')
$value.textContent = 0
}
else {
const percentage = art.volume * 100
setStyle($volume, 'display', 'flex')
setStyle($close, 'display', 'none')
setStyle($indicator, 'top', `${100 - percentage}%`)
setStyle($loaded, 'top', `${100 - percentage}%`)
$value.textContent = Math.floor(percentage)
}
}
update();
art.on('video:volumechange', update);
update()
art.on('video:volumechange', update)
proxy($volume, 'click', () => {
art.muted = true;
});
proxy($volume, 'click', () => {
art.muted = true
})
proxy($close, 'click', () => {
art.muted = false;
});
proxy($close, 'click', () => {
art.muted = false
})
if (isMobile) {
setStyle($panel, 'display', 'none');
} else {
let isDragging = false;
if (isMobile) {
setStyle($panel, 'display', 'none')
}
else {
let isDragging = false
proxy($slider, 'mousedown', (event) => {
isDragging = event.button === 0;
art.volume = getVolumeFromEvent(event);
});
proxy($slider, 'mousedown', (event) => {
isDragging = event.button === 0
art.volume = getVolumeFromEvent(event)
})
art.on('document:mousemove', (event) => {
if (isDragging) {
art.muted = false;
art.volume = getVolumeFromEvent(event);
}
});
art.on('document:mousemove', (event) => {
if (isDragging) {
art.muted = false
art.volume = getVolumeFromEvent(event)
}
})
art.on('document:mouseup', () => {
if (isDragging) {
isDragging = false;
}
});
}
},
});
art.on('document:mouseup', () => {
if (isDragging) {
isDragging = false
}
})
}
},
})
}
+56 -53
View File
@@ -1,59 +1,62 @@
import { includeFromEvent, isMobile } from '../utils';
import { includeFromEvent, isMobile } from '../utils'
export default function clickInit(art, events) {
const {
constructor,
template: { $player, $video },
} = art;
const {
constructor,
template: { $player, $video },
} = art
events.proxy(document, ['click', 'contextmenu'], (event) => {
if (includeFromEvent(event, $player)) {
art.isInput = event.target.tagName === 'INPUT';
art.isFocus = true;
art.emit('focus', event);
} else {
art.isInput = false;
art.isFocus = false;
art.emit('blur', event);
events.proxy(document, ['click', 'contextmenu'], (event) => {
if (includeFromEvent(event, $player)) {
art.isInput = event.target.tagName === 'INPUT'
art.isFocus = true
art.emit('focus', event)
}
else {
art.isInput = false
art.isFocus = false
art.emit('blur', event)
}
})
let clickTimes = []
events.proxy($video, 'click', (event) => {
const now = Date.now()
clickTimes.push(now)
const { MOBILE_CLICK_PLAY, DBCLICK_TIME, MOBILE_DBCLICK_PLAY, DBCLICK_FULLSCREEN } = constructor
const clicks = clickTimes.filter(t => now - t <= DBCLICK_TIME)
switch (clicks.length) {
case 1:
art.emit('click', event)
if (isMobile) {
if (!art.isLock && MOBILE_CLICK_PLAY) {
art.toggle()
}
}
});
let clickTimes = [];
events.proxy($video, 'click', (event) => {
const now = Date.now();
clickTimes.push(now);
const { MOBILE_CLICK_PLAY, DBCLICK_TIME, MOBILE_DBCLICK_PLAY, DBCLICK_FULLSCREEN } = constructor;
const clicks = clickTimes.filter((t) => now - t <= DBCLICK_TIME);
switch (clicks.length) {
case 1:
art.emit('click', event);
if (isMobile) {
if (!art.isLock && MOBILE_CLICK_PLAY) {
art.toggle();
}
} else {
art.toggle();
}
clickTimes = clicks;
break;
case 2:
art.emit('dblclick', event);
if (isMobile) {
if (!art.isLock && MOBILE_DBCLICK_PLAY) {
art.toggle();
}
} else {
if (DBCLICK_FULLSCREEN) {
art.fullscreen = !art.fullscreen;
}
}
clickTimes = [];
break;
default:
clickTimes = [];
else {
art.toggle()
}
});
clickTimes = clicks
break
case 2:
art.emit('dblclick', event)
if (isMobile) {
if (!art.isLock && MOBILE_DBCLICK_PLAY) {
art.toggle()
}
}
else {
if (DBCLICK_FULLSCREEN) {
art.fullscreen = !art.fullscreen
}
}
clickTimes = []
break
default:
clickTimes = []
}
})
}
@@ -1,9 +1,9 @@
export default function documentInit(art, events) {
events.proxy(document, 'mousemove', (event) => {
art.emit('document:mousemove', event);
});
events.proxy(document, 'mousemove', (event) => {
art.emit('document:mousemove', event)
})
events.proxy(document, 'mouseup', (event) => {
art.emit('document:mouseup', event);
});
events.proxy(document, 'mouseup', (event) => {
art.emit('document:mouseup', event)
})
}
+87 -84
View File
@@ -1,101 +1,104 @@
import { clamp, secondToTime, isMobile } from '../utils';
import { setCurrentTime } from '../control/progress';
import { setCurrentTime } from '../control/progress'
import { clamp, isMobile, secondToTime } from '../utils'
function GetSlideAngle(dx, dy) {
return (Math.atan2(dy, dx) * 180) / Math.PI;
return (Math.atan2(dy, dx) * 180) / Math.PI
}
function GetSlideDirection(startX, startY, endX, endY) {
var dy = startY - endY;
var dx = endX - startX;
var result = 0;
const dy = startY - endY
const dx = endX - startX
let result = 0
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
return result;
}
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
return result
}
var angle = GetSlideAngle(dx, dy);
if (angle >= -45 && angle < 45) {
result = 4;
} else if (angle >= 45 && angle < 135) {
result = 1;
} else if (angle >= -135 && angle < -45) {
result = 2;
} else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3;
}
const angle = GetSlideAngle(dx, dy)
if (angle >= -45 && angle < 45) {
result = 4
}
else if (angle >= 45 && angle < 135) {
result = 1
}
else if (angle >= -135 && angle < -45) {
result = 2
}
else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3
}
return result;
return result
}
export default function gestureInit(art, events) {
if (isMobile && !art.option.isLive) {
const { $video, $progress } = art.template;
if (isMobile && !art.option.isLive) {
const { $video, $progress } = art.template
let touchTarget = null;
let isDragging = false;
let startX = 0;
let startY = 0;
let startTime = 0;
let touchTarget = null
let isDragging = false
let startX = 0
let startY = 0
let startTime = 0
const onTouchStart = (event) => {
if (event.touches.length === 1 && !art.isLock) {
if (touchTarget === $progress) {
setCurrentTime(art, event);
}
isDragging = true;
const { pageX, pageY } = event.touches[0];
startX = pageX;
startY = pageY;
startTime = art.currentTime;
}
};
const onTouchMove = (event) => {
if (event.touches.length === 1 && isDragging && art.duration) {
const { pageX, pageY } = event.touches[0];
const direction = GetSlideDirection(startX, startY, pageX, pageY);
const isHorizontal = [3, 4].includes(direction);
const isVertical = [1, 2].includes(direction);
const isLegal = (isHorizontal && !art.isRotate) || (isVertical && art.isRotate);
if (isLegal) {
const ratioX = clamp((pageX - startX) / art.width, -1, 1);
const ratioY = clamp((pageY - startY) / art.height, -1, 1);
const ratio = art.isRotate ? ratioY : ratioX;
const TOUCH_MOVE_RATIO = touchTarget === $video ? art.constructor.TOUCH_MOVE_RATIO : 1;
const currentTime = clamp(startTime + art.duration * ratio * TOUCH_MOVE_RATIO, 0, art.duration);
art.seek = currentTime;
art.emit('setBar', 'played', clamp(currentTime / art.duration, 0, 1), event);
art.notice.show = `${secondToTime(currentTime)} / ${secondToTime(art.duration)}`;
}
}
};
const onTouchEnd = () => {
if (isDragging) {
startX = 0;
startY = 0;
startTime = 0;
isDragging = false;
touchTarget = null;
}
};
if (art.option.gesture) {
events.proxy($video, 'touchstart', (event) => {
touchTarget = $video;
onTouchStart(event);
});
events.proxy($video, 'touchmove', onTouchMove);
const onTouchStart = (event) => {
if (event.touches.length === 1 && !art.isLock) {
if (touchTarget === $progress) {
setCurrentTime(art, event)
}
events.proxy($progress, 'touchstart', (event) => {
touchTarget = $progress;
onTouchStart(event);
});
events.proxy($progress, 'touchmove', onTouchMove);
events.proxy(document, 'touchend', onTouchEnd);
isDragging = true
const { pageX, pageY } = event.touches[0]
startX = pageX
startY = pageY
startTime = art.currentTime
}
}
const onTouchMove = (event) => {
if (event.touches.length === 1 && isDragging && art.duration) {
const { pageX, pageY } = event.touches[0]
const direction = GetSlideDirection(startX, startY, pageX, pageY)
const isHorizontal = [3, 4].includes(direction)
const isVertical = [1, 2].includes(direction)
const isLegal = (isHorizontal && !art.isRotate) || (isVertical && art.isRotate)
if (isLegal) {
const ratioX = clamp((pageX - startX) / art.width, -1, 1)
const ratioY = clamp((pageY - startY) / art.height, -1, 1)
const ratio = art.isRotate ? ratioY : ratioX
const TOUCH_MOVE_RATIO = touchTarget === $video ? art.constructor.TOUCH_MOVE_RATIO : 1
const currentTime = clamp(startTime + art.duration * ratio * TOUCH_MOVE_RATIO, 0, art.duration)
art.seek = currentTime
art.emit('setBar', 'played', clamp(currentTime / art.duration, 0, 1), event)
art.notice.show = `${secondToTime(currentTime)} / ${secondToTime(art.duration)}`
}
}
}
const onTouchEnd = () => {
if (isDragging) {
startX = 0
startY = 0
startTime = 0
isDragging = false
touchTarget = null
}
}
if (art.option.gesture) {
events.proxy($video, 'touchstart', (event) => {
touchTarget = $video
onTouchStart(event)
})
events.proxy($video, 'touchmove', onTouchMove)
}
events.proxy($progress, 'touchstart', (event) => {
touchTarget = $progress
onTouchStart(event)
})
events.proxy($progress, 'touchmove', onTouchMove)
events.proxy(document, 'touchend', onTouchEnd)
}
}
+13 -13
View File
@@ -1,17 +1,17 @@
import { addClass, removeClass } from '../utils';
import { addClass, removeClass } from '../utils'
export default function hoverInit(art, events) {
const { $player } = art.template;
const { $player } = art.template
events.hover(
$player,
(event) => {
addClass($player, 'art-hover');
art.emit('hover', true, event);
},
(event) => {
removeClass($player, 'art-hover');
art.emit('hover', false, event);
},
);
events.hover(
$player,
(event) => {
addClass($player, 'art-hover')
art.emit('hover', true, event)
},
(event) => {
removeClass($player, 'art-hover')
art.emit('hover', false, event)
},
)
}
+47 -49
View File
@@ -1,61 +1,59 @@
import clickInit from './clickInit';
import hoverInit from './hoverInit';
import moveInit from './moveInit';
import resizeInit from './resizeInit';
import gestureInit from './gestureInit';
import viewInit from './viewInit';
import documentInit from './documentInit';
import updateInit from './updateInit';
import restoreInit from './restoreInit';
import clickInit from './clickInit'
import documentInit from './documentInit'
import gestureInit from './gestureInit'
import hoverInit from './hoverInit'
import moveInit from './moveInit'
import resizeInit from './resizeInit'
import updateInit from './updateInit'
import viewInit from './viewInit'
export default class Events {
constructor(art) {
this.destroyEvents = [];
this.proxy = this.proxy.bind(this);
this.hover = this.hover.bind(this);
constructor(art) {
this.destroyEvents = []
this.proxy = this.proxy.bind(this)
this.hover = this.hover.bind(this)
clickInit(art, this);
hoverInit(art, this);
moveInit(art, this);
resizeInit(art, this);
gestureInit(art, this);
viewInit(art, this);
documentInit(art, this);
updateInit(art, this);
restoreInit(art, this);
clickInit(art, this)
hoverInit(art, this)
moveInit(art, this)
resizeInit(art, this)
gestureInit(art, this)
viewInit(art, this)
documentInit(art, this)
updateInit(art, this)
}
proxy(target, name, callback, option = {}) {
if (Array.isArray(name)) {
return name.map(item => this.proxy(target, item, callback, option))
}
proxy(target, name, callback, option = {}) {
if (Array.isArray(name)) {
return name.map((item) => this.proxy(target, item, callback, option));
}
target.addEventListener(name, callback, option)
const destroy = () => target.removeEventListener(name, callback, option)
this.destroyEvents.push(destroy)
return destroy
}
target.addEventListener(name, callback, option);
const destroy = () => target.removeEventListener(name, callback, option);
this.destroyEvents.push(destroy);
return destroy;
hover(target, mouseenter, mouseleave) {
if (mouseenter) {
this.proxy(target, 'mouseenter', mouseenter)
}
if (mouseleave) {
this.proxy(target, 'mouseleave', mouseleave)
}
}
hover(target, mouseenter, mouseleave) {
if (mouseenter) {
this.proxy(target, 'mouseenter', mouseenter);
}
if (mouseleave) {
this.proxy(target, 'mouseleave', mouseleave);
}
remove(destroyEvent) {
const index = this.destroyEvents.indexOf(destroyEvent)
if (index > -1) {
destroyEvent()
this.destroyEvents.splice(index, 1)
}
}
remove(destroyEvent) {
const index = this.destroyEvents.indexOf(destroyEvent);
if (index > -1) {
destroyEvent();
this.destroyEvents.splice(index, 1);
}
}
destroy() {
for (let index = 0; index < this.destroyEvents.length; index++) {
this.destroyEvents[index]();
}
destroy() {
for (let index = 0; index < this.destroyEvents.length; index++) {
this.destroyEvents[index]()
}
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
export default function moveInit(art, events) {
const { $player } = art.template;
const { $player } = art.template
events.proxy($player, 'mousemove', (event) => {
art.emit('mousemove', event);
});
events.proxy($player, 'mousemove', (event) => {
art.emit('mousemove', event)
})
}
+15 -15
View File
@@ -1,20 +1,20 @@
import { debounce } from '../utils';
import { debounce } from '../utils'
export default function resizeInit(art, events) {
const { option, constructor } = art;
const { option, constructor } = art
art.on('resize', () => {
const { aspectRatio, notice } = art;
if (art.state === 'standard' && option.autoSize) {
art.autoSize();
}
art.aspectRatio = aspectRatio;
notice.show = '';
});
const resizeFn = debounce(() => art.emit('resize'), constructor.RESIZE_TIME);
events.proxy(window, ['orientationchange', 'resize'], () => resizeFn());
if (screen && screen.orientation && screen.orientation.onchange) {
events.proxy(screen.orientation, 'change', () => resizeFn());
art.on('resize', () => {
const { aspectRatio, notice } = art
if (art.state === 'standard' && option.autoSize) {
art.autoSize()
}
art.aspectRatio = aspectRatio
notice.show = ''
})
const resizeFn = debounce(() => art.emit('resize'), constructor.RESIZE_TIME)
events.proxy(window, ['orientationchange', 'resize'], () => resizeFn())
if (screen && screen.orientation && screen.orientation.onchange) {
events.proxy(screen.orientation, 'change', () => resizeFn())
}
}
@@ -1,3 +0,0 @@
export default function restoreInit(art, events) {
//
}
+14 -14
View File
@@ -1,19 +1,19 @@
export default function updateInit(art) {
if (art.constructor.USE_RAF) {
let timer = null;
if (art.constructor.USE_RAF) {
let timer = null;
(function update() {
if (art.playing) {
art.emit('raf');
}
(function update() {
if (art.playing) {
art.emit('raf')
}
if (!art.isDestroy) {
timer = requestAnimationFrame(update);
}
})();
if (!art.isDestroy) {
timer = requestAnimationFrame(update)
}
})()
art.on('destroy', () => {
cancelAnimationFrame(timer);
});
}
art.on('destroy', () => {
cancelAnimationFrame(timer)
})
}
}
+15 -15
View File
@@ -1,21 +1,21 @@
import { throttle, isInViewport } from '../utils';
import { isInViewport, throttle } from '../utils'
export default function viewInit(art, events) {
const {
option,
constructor,
template: { $container },
} = art;
const {
option,
constructor,
template: { $container },
} = art
const scrollFn = throttle(() => {
art.emit('view', isInViewport($container, constructor.SCROLL_GAP));
}, constructor.SCROLL_TIME);
const scrollFn = throttle(() => {
art.emit('view', isInViewport($container, constructor.SCROLL_GAP))
}, constructor.SCROLL_TIME)
events.proxy(window, 'scroll', () => scrollFn());
events.proxy(window, 'scroll', () => scrollFn())
art.on('view', (state) => {
if (option.autoMini) {
art.mini = !state;
}
});
art.on('view', (state) => {
if (option.autoMini) {
art.mini = !state
}
})
}
+78 -77
View File
@@ -1,88 +1,89 @@
import { isMobile } from './utils';
import { isMobile } from './utils'
export default class Hotkey {
constructor(art) {
this.art = art;
this.keys = {};
constructor(art) {
this.art = art
this.keys = {}
if (art.option.hotkey && !isMobile) {
this.init();
}
if (art.option.hotkey && !isMobile) {
this.init()
}
}
init() {
const { proxy, constructor } = this.art;
init() {
const { proxy, constructor } = this.art
this.add('Escape', () => {
if (this.art.fullscreenWeb) {
this.art.fullscreenWeb = false;
this.add('Escape', () => {
if (this.art.fullscreenWeb) {
this.art.fullscreenWeb = false
}
})
this.add('Space', () => {
this.art.toggle()
})
this.add('ArrowLeft', () => {
this.art.backward = constructor.SEEK_STEP
})
this.add('ArrowUp', () => {
this.art.volume += constructor.VOLUME_STEP
})
this.add('ArrowRight', () => {
this.art.forward = constructor.SEEK_STEP
})
this.add('ArrowDown', () => {
this.art.volume -= constructor.VOLUME_STEP
})
proxy(document, 'keydown', (event) => {
if (this.art.isFocus) {
const tag = document.activeElement.tagName.toUpperCase()
const editable = document.activeElement.getAttribute('contenteditable')
if (
tag !== 'INPUT'
&& tag !== 'TEXTAREA'
&& editable !== ''
&& editable !== 'true'
&& !event.altKey
&& !event.ctrlKey
&& !event.metaKey
&& !event.shiftKey
) {
const events = this.keys[event.code]
if (events) {
event.preventDefault()
for (let index = 0; index < events.length; index++) {
events[index].call(this.art, event)
}
});
this.add('Space', () => {
this.art.toggle();
});
this.add('ArrowLeft', () => {
this.art.backward = constructor.SEEK_STEP;
});
this.add('ArrowUp', () => {
this.art.volume += constructor.VOLUME_STEP;
});
this.add('ArrowRight', () => {
this.art.forward = constructor.SEEK_STEP;
});
this.add('ArrowDown', () => {
this.art.volume -= constructor.VOLUME_STEP;
});
proxy(document, 'keydown', (event) => {
if (this.art.isFocus) {
const tag = document.activeElement.tagName.toUpperCase();
const editable = document.activeElement.getAttribute('contenteditable');
if (
tag !== 'INPUT' &&
tag !== 'TEXTAREA' &&
editable !== '' &&
editable !== 'true' &&
!event.altKey &&
!event.ctrlKey &&
!event.metaKey &&
!event.shiftKey
) {
const events = this.keys[event.code];
if (events) {
event.preventDefault();
for (let index = 0; index < events.length; index++) {
events[index].call(this.art, event);
}
this.art.emit('hotkey', event);
}
}
}
this.art.emit('keydown', event);
});
}
add(key, event) {
if (this.keys[key]) {
this.keys[key].push(event);
} else {
this.keys[key] = [event];
this.art.emit('hotkey', event)
}
}
return this;
}
}
this.art.emit('keydown', event)
})
}
remove(key, event) {
if (this.keys[key]) {
const index = this.keys[key].indexOf(event);
if (index !== -1) {
this.keys[key].splice(index, 1);
}
}
return this;
add(key, event) {
if (this.keys[key]) {
this.keys[key].push(event)
}
else {
this.keys[key] = [event]
}
return this
}
remove(key, event) {
if (this.keys[key]) {
const index = this.keys[key].indexOf(event)
if (index !== -1) {
this.keys[key].splice(index, 1)
}
}
return this
}
}
+39 -39
View File
@@ -1,44 +1,44 @@
const ar = {
'Video Info': 'معلومات الفيديو',
Close: 'إغلاق',
'Video Load Failed': 'فشل تحميل الفيديو',
Volume: 'الصوت',
Play: 'تشغيل',
Pause: 'إيقاف مؤقت',
Rate: 'السرعة',
Mute: 'كتم الصوت',
'Video Flip': 'تدوير الفيديو',
Horizontal: 'أفقي',
Vertical: 'عمودي',
Reconnect: 'إعادة الاتصال',
'Show Setting': 'إظهار الإعدادات',
'Hide Setting': 'إخفاء الإعدادات',
Screenshot: 'لقطة شاشة',
'Play Speed': 'سرعة التشغيل',
'Aspect Ratio': 'نسبة العرض إلى الارتفاع',
Default: 'افتراضي',
Normal: 'عادي',
Open: 'فتح',
'Switch Video': 'تبديل الفيديو',
'Switch Subtitle': 'تبديل الترجمة',
Fullscreen: 'وضع ملء الشاشة',
'Exit Fullscreen': 'الخروج من وضع ملء الشاشة',
'Web Fullscreen': 'وضع ملء الشاشة في المتصفح',
'Exit Web Fullscreen': 'الخروج من وضع ملء الشاشة في المتصفح',
'Mini Player': 'مشغل صغير',
'PIP Mode': 'الوضع العائم',
'Exit PIP Mode': 'الخروج من الوضع العائم',
'PIP Not Supported': 'الوضع العائم غير مدعوم',
'Fullscreen Not Supported': 'وضع ملء الشاشة غير مدعوم',
'Subtitle Offset': 'إزاحة الترجمة',
'Last Seen': 'آخر مشاهدة',
'Jump Play': 'التشغيل القافز',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay غير متاح',
};
'Video Info': 'معلومات الفيديو',
'Close': 'إغلاق',
'Video Load Failed': 'فشل تحميل الفيديو',
'Volume': 'الصوت',
'Play': 'تشغيل',
'Pause': 'إيقاف مؤقت',
'Rate': 'السرعة',
'Mute': 'كتم الصوت',
'Video Flip': 'تدوير الفيديو',
'Horizontal': 'أفقي',
'Vertical': 'عمودي',
'Reconnect': 'إعادة الاتصال',
'Show Setting': 'إظهار الإعدادات',
'Hide Setting': 'إخفاء الإعدادات',
'Screenshot': 'لقطة شاشة',
'Play Speed': 'سرعة التشغيل',
'Aspect Ratio': 'نسبة العرض إلى الارتفاع',
'Default': 'افتراضي',
'Normal': 'عادي',
'Open': 'فتح',
'Switch Video': 'تبديل الفيديو',
'Switch Subtitle': 'تبديل الترجمة',
'Fullscreen': 'وضع ملء الشاشة',
'Exit Fullscreen': 'الخروج من وضع ملء الشاشة',
'Web Fullscreen': 'وضع ملء الشاشة في المتصفح',
'Exit Web Fullscreen': 'الخروج من وضع ملء الشاشة في المتصفح',
'Mini Player': 'مشغل صغير',
'PIP Mode': 'الوضع العائم',
'Exit PIP Mode': 'الخروج من الوضع العائم',
'PIP Not Supported': 'الوضع العائم غير مدعوم',
'Fullscreen Not Supported': 'وضع ملء الشاشة غير مدعوم',
'Subtitle Offset': 'إزاحة الترجمة',
'Last Seen': 'آخر مشاهدة',
'Jump Play': 'التشغيل القافز',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay غير متاح',
}
export default ar;
export default ar
if (typeof window !== 'undefined') {
window['artplayer-i18n-ar'] = ar;
window['artplayer-i18n-ar'] = ar
}
+39 -39
View File
@@ -1,44 +1,44 @@
const cs = {
'Video Info': 'Info o videu',
Close: 'Zavřít',
'Video Load Failed': 'Nahrání videa selhalo',
Volume: 'Hlasitost',
Play: 'Přehrát',
Pause: 'Pozastavit',
Rate: 'Hodnocení',
Mute: 'Ztlumit',
'Video Flip': 'Otočit video',
Horizontal: 'Horizontálně',
Vertical: 'Vertikálně',
Reconnect: 'Opětovné připojení',
'Show Setting': 'Zobrazit nastavení',
'Hide Setting': 'Skrýt nastavení',
Screenshot: 'Snímek obrazovky',
'Play Speed': 'Rychlost přehrávání',
'Aspect Ratio': 'Poměr stran',
Default: 'Výchozí',
Normal: 'Normální',
Open: 'Otevřít',
'Switch Video': 'Přepnout video',
'Switch Subtitle': 'Přepnout titulky',
Fullscreen: 'Celá obrazovka',
'Exit Fullscreen': 'Opustit režim celé obrazovky',
'Web Fullscreen': 'Celá stránka',
'Exit Web Fullscreen': 'Zavřít režim celé stránky',
'Mini Player': 'Mini přehrávač',
'PIP Mode': 'Režim PIP',
'Exit PIP Mode': 'Opustit režim PIP',
'PIP Not Supported': 'Režim PIP není podporován',
'Fullscreen Not Supported': 'Režim celé obrazovky není podporován',
'Subtitle Offset': 'Posun titulků',
'Last Seen': 'Naposledy viděn',
'Jump Play': 'Hra na skok',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay není k dispozici',
};
'Video Info': 'Info o videu',
'Close': 'Zavřít',
'Video Load Failed': 'Nahrání videa selhalo',
'Volume': 'Hlasitost',
'Play': 'Přehrát',
'Pause': 'Pozastavit',
'Rate': 'Hodnocení',
'Mute': 'Ztlumit',
'Video Flip': 'Otočit video',
'Horizontal': 'Horizontálně',
'Vertical': 'Vertikálně',
'Reconnect': 'Opětovné připojení',
'Show Setting': 'Zobrazit nastavení',
'Hide Setting': 'Skrýt nastavení',
'Screenshot': 'Snímek obrazovky',
'Play Speed': 'Rychlost přehrávání',
'Aspect Ratio': 'Poměr stran',
'Default': 'Výchozí',
'Normal': 'Normální',
'Open': 'Otevřít',
'Switch Video': 'Přepnout video',
'Switch Subtitle': 'Přepnout titulky',
'Fullscreen': 'Celá obrazovka',
'Exit Fullscreen': 'Opustit režim celé obrazovky',
'Web Fullscreen': 'Celá stránka',
'Exit Web Fullscreen': 'Zavřít režim celé stránky',
'Mini Player': 'Mini přehrávač',
'PIP Mode': 'Režim PIP',
'Exit PIP Mode': 'Opustit režim PIP',
'PIP Not Supported': 'Režim PIP není podporován',
'Fullscreen Not Supported': 'Režim celé obrazovky není podporován',
'Subtitle Offset': 'Posun titulků',
'Last Seen': 'Naposledy viděn',
'Jump Play': 'Hra na skok',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay není k dispozici',
}
export default cs;
export default cs
if (typeof window !== 'undefined') {
window['artplayer-i18n-cs'] = cs;
window['artplayer-i18n-cs'] = cs
}
+39 -39
View File
@@ -1,44 +1,44 @@
const es = {
'Video Info': 'Información del video',
Close: 'Cerrar',
'Video Load Failed': 'Falló carga de video',
Volume: 'Volumen',
Play: 'Reproduciendo',
Pause: 'Pausa',
Rate: 'Velocidad',
Mute: 'Silencio',
'Video Flip': 'Rotar video',
Horizontal: 'Horizontal',
Vertical: 'Vertical',
Reconnect: 'Reconectando',
'Show Setting': 'Mostrar ajustes',
'Hide Setting': 'Ocultar ajustes',
Screenshot: 'Captura de Pantalla',
'Play Speed': 'Velocidad de reproducción',
'Aspect Ratio': 'Relación de aspecto',
Default: 'Por defecto',
Normal: 'Normal',
Open: 'Abrir',
'Switch Video': 'Cambiar video',
'Switch Subtitle': 'Cambiar subtítulo',
Fullscreen: 'Pantalla completa',
'Exit Fullscreen': 'Salir de Pantalla completa',
'Web Fullscreen': 'Pantalla completa Web',
'Exit Web Fullscreen': 'Salir de Pantalla completa',
'Mini Player': 'Mini reproductor',
'PIP Mode': 'Modo PiP',
'Exit PIP Mode': 'Cerrar modo PiP',
'PIP Not Supported': 'Modo PiP no compatible',
'Fullscreen Not Supported': 'Pantalla completa no soportada',
'Subtitle Offset': 'Ajuste subtítulo',
'Last Seen': 'Visto última vez',
'Jump Play': 'Saltar',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay no disponible',
};
'Video Info': 'Información del video',
'Close': 'Cerrar',
'Video Load Failed': 'Falló carga de video',
'Volume': 'Volumen',
'Play': 'Reproduciendo',
'Pause': 'Pausa',
'Rate': 'Velocidad',
'Mute': 'Silencio',
'Video Flip': 'Rotar video',
'Horizontal': 'Horizontal',
'Vertical': 'Vertical',
'Reconnect': 'Reconectando',
'Show Setting': 'Mostrar ajustes',
'Hide Setting': 'Ocultar ajustes',
'Screenshot': 'Captura de Pantalla',
'Play Speed': 'Velocidad de reproducción',
'Aspect Ratio': 'Relación de aspecto',
'Default': 'Por defecto',
'Normal': 'Normal',
'Open': 'Abrir',
'Switch Video': 'Cambiar video',
'Switch Subtitle': 'Cambiar subtítulo',
'Fullscreen': 'Pantalla completa',
'Exit Fullscreen': 'Salir de Pantalla completa',
'Web Fullscreen': 'Pantalla completa Web',
'Exit Web Fullscreen': 'Salir de Pantalla completa',
'Mini Player': 'Mini reproductor',
'PIP Mode': 'Modo PiP',
'Exit PIP Mode': 'Cerrar modo PiP',
'PIP Not Supported': 'Modo PiP no compatible',
'Fullscreen Not Supported': 'Pantalla completa no soportada',
'Subtitle Offset': 'Ajuste subtítulo',
'Last Seen': 'Visto última vez',
'Jump Play': 'Saltar',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay no disponible',
}
export default es;
export default es
if (typeof window !== 'undefined') {
window['artplayer-i18n-es'] = es;
window['artplayer-i18n-es'] = es
}
+39 -39
View File
@@ -1,44 +1,44 @@
const fa = {
'Video Info': 'اطلاعات ویدیو',
Close: 'بستن',
'Video Load Failed': 'بارگذاری ناموفق',
Play: 'پخش',
Volume: 'میزان صدا',
Pause: 'توقف',
Rate: 'نرخ',
Mute: 'سکوت',
'Video Flip': 'چرخش تصویر',
Horizontal: 'افقی',
Vertical: 'عمودی',
Reconnect: 'اتصال مجدد',
'Show Setting': 'تنظیمات',
'Hide Setting': 'بستن تنظیمات',
Screenshot: 'عکس از صفحه',
'Play Speed': 'سرعت پخش',
'Aspect Ratio': 'نسبت تصویر',
Default: 'حالت پیشفرض',
Normal: ' حالت عادی',
Open: 'بازکردن',
'Switch Video': 'تغییر ویدیو',
'Switch Subtitle': 'نغییر زیرنویس',
Fullscreen: 'تمام صفحه',
'Exit Fullscreen': 'کوچک کردن',
'Web Fullscreen': 'حالت تئاتر',
'Exit Web Fullscreen': 'خروج از حالت تئاتر',
'Mini Player': 'حالت پخش کوچک',
'PIP Mode': ' مینی پلیر',
'Exit PIP Mode': 'خروج از مینی پلیر',
'PIP Not Supported': 'عدم پشتیبانی از مینی پلیر',
'Fullscreen Not Supported': 'عدم پشتیبانی از حالت تمام صفحه',
'Subtitle Offset': 'افست زیرنویس',
'Last Seen': 'آخرین بازدید',
'Jump Play': 'جامپ پلی',
AirPlay: 'ایر پلی',
'AirPlay Not Available': 'عدم پشتیبانی از ایرپلی',
};
'Video Info': 'اطلاعات ویدیو',
'Close': 'بستن',
'Video Load Failed': 'بارگذاری ناموفق',
'Play': 'پخش',
'Volume': 'میزان صدا',
'Pause': 'توقف',
'Rate': 'نرخ',
'Mute': 'سکوت',
'Video Flip': 'چرخش تصویر',
'Horizontal': 'افقی',
'Vertical': 'عمودی',
'Reconnect': 'اتصال مجدد',
'Show Setting': 'تنظیمات',
'Hide Setting': 'بستن تنظیمات',
'Screenshot': 'عکس از صفحه',
'Play Speed': 'سرعت پخش',
'Aspect Ratio': 'نسبت تصویر',
'Default': 'حالت پیشفرض',
'Normal': ' حالت عادی',
'Open': 'بازکردن',
'Switch Video': 'تغییر ویدیو',
'Switch Subtitle': 'نغییر زیرنویس',
'Fullscreen': 'تمام صفحه',
'Exit Fullscreen': 'کوچک کردن',
'Web Fullscreen': 'حالت تئاتر',
'Exit Web Fullscreen': 'خروج از حالت تئاتر',
'Mini Player': 'حالت پخش کوچک',
'PIP Mode': ' مینی پلیر',
'Exit PIP Mode': 'خروج از مینی پلیر',
'PIP Not Supported': 'عدم پشتیبانی از مینی پلیر',
'Fullscreen Not Supported': 'عدم پشتیبانی از حالت تمام صفحه',
'Subtitle Offset': 'افست زیرنویس',
'Last Seen': 'آخرین بازدید',
'Jump Play': 'جامپ پلی',
'AirPlay': 'ایر پلی',
'AirPlay Not Available': 'عدم پشتیبانی از ایرپلی',
}
export default fa;
export default fa
if (typeof window !== 'undefined') {
window['artplayer-i18n-fa'] = fa;
window['artplayer-i18n-fa'] = fa
}
+39 -39
View File
@@ -1,44 +1,44 @@
const fr = {
'Video Info': 'Informations de la vidéo',
Close: 'Fermer',
'Video Load Failed': 'Téléchargement de la vidéo échoué',
Volume: 'Volume',
Play: 'Lire',
Pause: 'Pause',
Rate: 'Vitesse',
Mute: 'Muet',
'Video Flip': 'Rotation de la vidéo',
Horizontal: 'Horizontal',
Vertical: 'Vertical',
Reconnect: 'Reconnexion',
'Show Setting': 'Afficher les paramètres',
'Hide Setting': 'Cacher les paramètres',
Screenshot: "Capture d'écran",
'Play Speed': 'Vitesse de lecture',
'Aspect Ratio': "Rapport d'aspect",
Default: 'Défaut',
Normal: 'Normal',
Open: 'Ouvrir',
'Switch Video': 'Basculer la vidéo',
'Switch Subtitle': 'Basculer le sous-titre',
Fullscreen: 'Plein écran',
'Exit Fullscreen': 'Quitter le plein écran',
'Web Fullscreen': 'Plein écran Web',
'Exit Web Fullscreen': 'Quitter le plein écran Web',
'Mini Player': 'Mini lecteur',
'PIP Mode': 'Mode PiP',
'Exit PIP Mode': 'Fermer le mode PiP',
'PIP Not Supported': 'Mode PiP non supporté',
'Fullscreen Not Supported': 'Plein écran non supporté',
'Subtitle Offset': 'Réglage des sous-titres',
'Last Seen': 'Dernière position',
'Jump Play': 'Continuer',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay non disponible',
};
'Video Info': 'Informations de la vidéo',
'Close': 'Fermer',
'Video Load Failed': 'Téléchargement de la vidéo échoué',
'Volume': 'Volume',
'Play': 'Lire',
'Pause': 'Pause',
'Rate': 'Vitesse',
'Mute': 'Muet',
'Video Flip': 'Rotation de la vidéo',
'Horizontal': 'Horizontal',
'Vertical': 'Vertical',
'Reconnect': 'Reconnexion',
'Show Setting': 'Afficher les paramètres',
'Hide Setting': 'Cacher les paramètres',
'Screenshot': 'Capture d\'écran',
'Play Speed': 'Vitesse de lecture',
'Aspect Ratio': 'Rapport d\'aspect',
'Default': 'Défaut',
'Normal': 'Normal',
'Open': 'Ouvrir',
'Switch Video': 'Basculer la vidéo',
'Switch Subtitle': 'Basculer le sous-titre',
'Fullscreen': 'Plein écran',
'Exit Fullscreen': 'Quitter le plein écran',
'Web Fullscreen': 'Plein écran Web',
'Exit Web Fullscreen': 'Quitter le plein écran Web',
'Mini Player': 'Mini lecteur',
'PIP Mode': 'Mode PiP',
'Exit PIP Mode': 'Fermer le mode PiP',
'PIP Not Supported': 'Mode PiP non supporté',
'Fullscreen Not Supported': 'Plein écran non supporté',
'Subtitle Offset': 'Réglage des sous-titres',
'Last Seen': 'Dernière position',
'Jump Play': 'Continuer',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay non disponible',
}
export default fr;
export default fr
if (typeof window !== 'undefined') {
window['artplayer-i18n-fr'] = fr;
window['artplayer-i18n-fr'] = fr
}
+39 -39
View File
@@ -1,44 +1,44 @@
const id = {
'Video Info': 'Informasi Video',
Close: 'Tutup',
'Video Load Failed': 'Gagal Memuat Video',
Volume: 'Volume',
Play: 'Putar',
Pause: 'Jeda',
Rate: 'Kecepatan',
Mute: 'Senyap',
'Video Flip': 'Memutar Video',
Horizontal: 'Horizontal',
Vertical: 'Vertikal',
Reconnect: 'Menyambung Kembali',
'Show Setting': 'Tampilkan Pengaturan',
'Hide Setting': 'Sembunyikan Pengaturan',
Screenshot: 'Tangkapan Layar',
'Play Speed': 'Kecepatan Putar',
'Aspect Ratio': 'Rasio Aspek',
Default: 'Default',
Normal: 'Normal',
Open: 'Buka',
'Switch Video': 'Ganti Video',
'Switch Subtitle': 'Ganti Subtitle',
Fullscreen: 'Layar Penuh',
'Exit Fullscreen': 'Keluar dari Layar Penuh',
'Web Fullscreen': 'Layar Penuh Web',
'Exit Web Fullscreen': 'Keluar dari Layar Penuh Web',
'Mini Player': 'Pemutar Mini',
'PIP Mode': 'Mode PIP',
'Exit PIP Mode': 'Keluar dari Mode PIP',
'PIP Not Supported': 'PIP Tidak Didukung',
'Fullscreen Not Supported': 'Layar Penuh Tidak Didukung',
'Subtitle Offset': 'Pergeseran Subtitle',
'Last Seen': 'Terakhir Dilihat',
'Jump Play': 'Lompat Putar',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay Tidak Tersedia',
};
'Video Info': 'Informasi Video',
'Close': 'Tutup',
'Video Load Failed': 'Gagal Memuat Video',
'Volume': 'Volume',
'Play': 'Putar',
'Pause': 'Jeda',
'Rate': 'Kecepatan',
'Mute': 'Senyap',
'Video Flip': 'Memutar Video',
'Horizontal': 'Horizontal',
'Vertical': 'Vertikal',
'Reconnect': 'Menyambung Kembali',
'Show Setting': 'Tampilkan Pengaturan',
'Hide Setting': 'Sembunyikan Pengaturan',
'Screenshot': 'Tangkapan Layar',
'Play Speed': 'Kecepatan Putar',
'Aspect Ratio': 'Rasio Aspek',
'Default': 'Default',
'Normal': 'Normal',
'Open': 'Buka',
'Switch Video': 'Ganti Video',
'Switch Subtitle': 'Ganti Subtitle',
'Fullscreen': 'Layar Penuh',
'Exit Fullscreen': 'Keluar dari Layar Penuh',
'Web Fullscreen': 'Layar Penuh Web',
'Exit Web Fullscreen': 'Keluar dari Layar Penuh Web',
'Mini Player': 'Pemutar Mini',
'PIP Mode': 'Mode PIP',
'Exit PIP Mode': 'Keluar dari Mode PIP',
'PIP Not Supported': 'PIP Tidak Didukung',
'Fullscreen Not Supported': 'Layar Penuh Tidak Didukung',
'Subtitle Offset': 'Pergeseran Subtitle',
'Last Seen': 'Terakhir Dilihat',
'Jump Play': 'Lompat Putar',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay Tidak Tersedia',
}
export default id;
export default id
if (typeof window !== 'undefined') {
window['artplayer-i18n-id'] = id;
window['artplayer-i18n-id'] = id
}
+22 -22
View File
@@ -1,30 +1,30 @@
import { mergeDeep } from '../utils';
import zhCn from './zh-cn';
import { mergeDeep } from '../utils'
import zhCn from './zh-cn'
export default class I18n {
constructor(art) {
this.art = art;
constructor(art) {
this.art = art
this.languages = {
'zh-cn': zhCn,
};
this.language = {};
this.update(art.option.i18n);
this.languages = {
'zh-cn': zhCn,
}
init() {
const lang = this.art.option.lang.toLowerCase();
this.language = this.languages[lang] || {};
}
this.language = {}
get(key) {
return this.language[key] || key;
}
this.update(art.option.i18n)
}
update(value) {
this.languages = mergeDeep(this.languages, value);
this.init();
}
init() {
const lang = this.art.option.lang.toLowerCase()
this.language = this.languages[lang] || {}
}
get(key) {
return this.language[key] || key
}
update(value) {
this.languages = mergeDeep(this.languages, value)
this.init()
}
}
+39 -39
View File
@@ -1,44 +1,44 @@
const pl = {
'Video Info': 'Informacje o wideo',
Close: 'Zamknij',
'Video Load Failed': 'Błąd ładowania wideo',
Volume: 'Głośność',
Play: 'Odtwórz',
Pause: 'Wstrzymaj',
Rate: 'Oceń',
Mute: 'Wycisz',
'Video Flip': 'Rotacja wideo',
Horizontal: 'Pozioma',
Vertical: 'Pionowa',
Reconnect: 'Połącz ponownie',
'Show Setting': 'Pokaż ustawienia',
'Hide Setting': 'Ukryj ustawienia',
Screenshot: 'Zrzut ekranu',
'Play Speed': 'Prędkość odtwarzania',
'Aspect Ratio': 'Współczynnik proporcji',
Default: 'Domyślny',
Normal: 'Normalny',
Open: 'Otwórz',
'Switch Video': 'Przełącz wideo',
'Switch Subtitle': 'Przełącz napisy',
Fullscreen: 'Pełny ekran',
'Exit Fullscreen': 'Zamknij pełny ekran',
'Web Fullscreen': 'Tryb pełnej strony',
'Exit Web Fullscreen': 'Zamknij tryb pełnej strony',
'Mini Player': 'Miniodtwarzacz',
'PIP Mode': 'Tryb PiP',
'Exit PIP Mode': 'Zamknij tryb PiP',
'PIP Not Supported': 'Tryb PiP nieobsługiwany',
'Fullscreen Not Supported': 'Pełny ekran nieobsługiwany',
'Subtitle Offset': 'Przesunięcie napisów',
'Last Seen': 'Ostatnio widziany',
'Jump Play': 'Skocz do gry',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay nie jest dostępny',
};
'Video Info': 'Informacje o wideo',
'Close': 'Zamknij',
'Video Load Failed': 'Błąd ładowania wideo',
'Volume': 'Głośność',
'Play': 'Odtwórz',
'Pause': 'Wstrzymaj',
'Rate': 'Oceń',
'Mute': 'Wycisz',
'Video Flip': 'Rotacja wideo',
'Horizontal': 'Pozioma',
'Vertical': 'Pionowa',
'Reconnect': 'Połącz ponownie',
'Show Setting': 'Pokaż ustawienia',
'Hide Setting': 'Ukryj ustawienia',
'Screenshot': 'Zrzut ekranu',
'Play Speed': 'Prędkość odtwarzania',
'Aspect Ratio': 'Współczynnik proporcji',
'Default': 'Domyślny',
'Normal': 'Normalny',
'Open': 'Otwórz',
'Switch Video': 'Przełącz wideo',
'Switch Subtitle': 'Przełącz napisy',
'Fullscreen': 'Pełny ekran',
'Exit Fullscreen': 'Zamknij pełny ekran',
'Web Fullscreen': 'Tryb pełnej strony',
'Exit Web Fullscreen': 'Zamknij tryb pełnej strony',
'Mini Player': 'Miniodtwarzacz',
'PIP Mode': 'Tryb PiP',
'Exit PIP Mode': 'Zamknij tryb PiP',
'PIP Not Supported': 'Tryb PiP nieobsługiwany',
'Fullscreen Not Supported': 'Pełny ekran nieobsługiwany',
'Subtitle Offset': 'Przesunięcie napisów',
'Last Seen': 'Ostatnio widziany',
'Jump Play': 'Skocz do gry',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay nie jest dostępny',
}
export default pl;
export default pl
if (typeof window !== 'undefined') {
window['artplayer-i18n-pl'] = pl;
window['artplayer-i18n-pl'] = pl
}
+39 -39
View File
@@ -1,44 +1,44 @@
const ru = {
'Video Info': 'Информация',
Close: 'Закрыть',
'Video Load Failed': 'Ошибка загрузки видео',
Volume: 'Громкость',
Play: 'Играть',
Pause: 'Пауза',
Rate: 'Скорость',
Mute: 'Заглушить',
'Video Flip': 'Развернуть видео',
Horizontal: 'Горизонтально',
Vertical: 'Вертикально',
Reconnect: 'Переподключенине',
'Show Setting': 'Показать настройки',
'Hide Setting': 'Скрыть настройки',
Screenshot: 'Скриншот',
'Play Speed': 'Скорость воспроизведения',
'Aspect Ratio': 'Соотношение сторон',
Default: 'По-умолчанию',
Normal: 'Нормальный',
Open: 'Открыть',
'Switch Video': 'Переключить видео',
'Switch Subtitle': 'Переключить субтитры',
Fullscreen: 'Полноэкранный режим',
'Exit Fullscreen': 'Выход из полноэкранного режима',
'Web Fullscreen': 'На все окно браузера',
'Exit Web Fullscreen': 'Выход из режима полного окна',
'Mini Player': 'Мини проигрыватель',
'PIP Mode': 'Картинка в картинке',
'Exit PIP Mode': 'Закрыть картинку в картинке',
'PIP Not Supported': 'Картинка в картинке не поддерживается',
'Fullscreen Not Supported': 'Полноэкранный режим не поддерживается',
'Subtitle Offset': 'Настройка субтитров',
'Last Seen': 'Последнее просмотренное',
'Jump Play': 'Перейти',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay недоступен',
};
'Video Info': 'Информация',
'Close': 'Закрыть',
'Video Load Failed': 'Ошибка загрузки видео',
'Volume': 'Громкость',
'Play': 'Играть',
'Pause': 'Пауза',
'Rate': 'Скорость',
'Mute': 'Заглушить',
'Video Flip': 'Развернуть видео',
'Horizontal': 'Горизонтально',
'Vertical': 'Вертикально',
'Reconnect': 'Переподключенине',
'Show Setting': 'Показать настройки',
'Hide Setting': 'Скрыть настройки',
'Screenshot': 'Скриншот',
'Play Speed': 'Скорость воспроизведения',
'Aspect Ratio': 'Соотношение сторон',
'Default': 'По-умолчанию',
'Normal': 'Нормальный',
'Open': 'Открыть',
'Switch Video': 'Переключить видео',
'Switch Subtitle': 'Переключить субтитры',
'Fullscreen': 'Полноэкранный режим',
'Exit Fullscreen': 'Выход из полноэкранного режима',
'Web Fullscreen': 'На все окно браузера',
'Exit Web Fullscreen': 'Выход из режима полного окна',
'Mini Player': 'Мини проигрыватель',
'PIP Mode': 'Картинка в картинке',
'Exit PIP Mode': 'Закрыть картинку в картинке',
'PIP Not Supported': 'Картинка в картинке не поддерживается',
'Fullscreen Not Supported': 'Полноэкранный режим не поддерживается',
'Subtitle Offset': 'Настройка субтитров',
'Last Seen': 'Последнее просмотренное',
'Jump Play': 'Перейти',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay недоступен',
}
export default ru;
export default ru
if (typeof window !== 'undefined') {
window['artplayer-i18n-ru'] = ru;
window['artplayer-i18n-ru'] = ru
}
+39 -39
View File
@@ -1,44 +1,44 @@
const tr = {
'Video Info': 'Video Bilgisi',
Close: 'Kapat',
'Video Load Failed': 'Video Yüklenemedi',
Volume: 'Ses Seviyesi',
Play: 'Oynat',
Pause: 'Duraklat',
Rate: 'Hız',
Mute: 'Sessize Al',
'Video Flip': 'Videoyu Çevir',
Horizontal: 'Yatay',
Vertical: 'Dikey',
Reconnect: 'Yeniden Bağlan',
'Show Setting': 'Ayarları Göster',
'Hide Setting': 'Ayarları Gizle',
Screenshot: 'Ekran Görüntüsü',
'Play Speed': 'Oynatma Hızı',
'Aspect Ratio': 'En Boy Oranı',
Default: 'Varsayılan',
Normal: 'Normal',
Open: 'Aç',
'Switch Video': 'Video Değiştir',
'Switch Subtitle': 'Altyazı Değiştir',
Fullscreen: 'Tam Ekran',
'Exit Fullscreen': 'Tam Ekrandan Çık',
'Web Fullscreen': 'Web Tam Ekran',
'Exit Web Fullscreen': 'Web Tam Ekranından Çık',
'Mini Player': 'Küçük Oynatıcı',
'PIP Mode': 'PIP Modu',
'Exit PIP Mode': 'PIP Modundan Çık',
'PIP Not Supported': 'PIP Desteklenmiyor',
'Fullscreen Not Supported': 'Tam Ekran Desteklenmiyor',
'Subtitle Offset': 'Altyazı Kaydırma',
'Last Seen': 'Son İzlenen',
'Jump Play': 'Atla ve Oynat',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay Kullanılamıyor',
};
'Video Info': 'Video Bilgisi',
'Close': 'Kapat',
'Video Load Failed': 'Video Yüklenemedi',
'Volume': 'Ses Seviyesi',
'Play': 'Oynat',
'Pause': 'Duraklat',
'Rate': 'Hız',
'Mute': 'Sessize Al',
'Video Flip': 'Videoyu Çevir',
'Horizontal': 'Yatay',
'Vertical': 'Dikey',
'Reconnect': 'Yeniden Bağlan',
'Show Setting': 'Ayarları Göster',
'Hide Setting': 'Ayarları Gizle',
'Screenshot': 'Ekran Görüntüsü',
'Play Speed': 'Oynatma Hızı',
'Aspect Ratio': 'En Boy Oranı',
'Default': 'Varsayılan',
'Normal': 'Normal',
'Open': 'Aç',
'Switch Video': 'Video Değiştir',
'Switch Subtitle': 'Altyazı Değiştir',
'Fullscreen': 'Tam Ekran',
'Exit Fullscreen': 'Tam Ekrandan Çık',
'Web Fullscreen': 'Web Tam Ekran',
'Exit Web Fullscreen': 'Web Tam Ekranından Çık',
'Mini Player': 'Küçük Oynatıcı',
'PIP Mode': 'PIP Modu',
'Exit PIP Mode': 'PIP Modundan Çık',
'PIP Not Supported': 'PIP Desteklenmiyor',
'Fullscreen Not Supported': 'Tam Ekran Desteklenmiyor',
'Subtitle Offset': 'Altyazı Kaydırma',
'Last Seen': 'Son İzlenen',
'Jump Play': 'Atla ve Oynat',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay Kullanılamıyor',
}
export default tr;
export default tr
if (typeof window !== 'undefined') {
window['artplayer-i18n-tr'] = tr;
window['artplayer-i18n-tr'] = tr
}
+39 -39
View File
@@ -1,44 +1,44 @@
const vi = {
'Video Info': 'Thông tin video',
Close: 'Đóng',
'Video Load Failed': 'Tải video thất bại',
Volume: 'Âm lượng',
Play: 'Phát',
Pause: 'Tạm dừng',
Rate: 'Tốc độ',
Mute: 'Tắt tiếng',
'Video Flip': 'Lật video',
Horizontal: 'Ngang',
Vertical: 'Dọc',
Reconnect: 'Kết nối lại',
'Show Setting': 'Cài đặt',
'Hide Setting': 'Ẩn cài đặt',
Screenshot: 'Chụp màn hình',
'Play Speed': 'Tốc độ phát',
'Aspect Ratio': 'Tỷ lệ khung hình',
Default: 'Mặc định',
Normal: 'Bình thường',
Open: 'Mở',
'Switch Video': 'Chuyển video',
'Switch Subtitle': 'Chuyển phụ đề',
Fullscreen: 'Toàn màn hình',
'Exit Fullscreen': 'Thoát toàn màn hình',
'Web Fullscreen': 'Toàn màn hình trình duyệt',
'Exit Web Fullscreen': 'Thoát toàn màn hình trình duyệt',
'Mini Player': 'Trình phát mini',
'PIP Mode': 'Phát trong hình',
'Exit PIP Mode': 'Thoát phát trong hình',
'PIP Not Supported': 'Không hỗ trợ phát trong hình',
'Fullscreen Not Supported': 'Không hỗ trợ toàn màn hình',
'Subtitle Offset': 'Độ trễ phụ đề',
'Last Seen': 'Lần xem cuối',
'Jump Play': 'Nhảy đến đoạn phát',
AirPlay: 'AirPlay',
'AirPlay Not Available': 'AirPlay không khả dụng',
};
'Video Info': 'Thông tin video',
'Close': 'Đóng',
'Video Load Failed': 'Tải video thất bại',
'Volume': 'Âm lượng',
'Play': 'Phát',
'Pause': 'Tạm dừng',
'Rate': 'Tốc độ',
'Mute': 'Tắt tiếng',
'Video Flip': 'Lật video',
'Horizontal': 'Ngang',
'Vertical': 'Dọc',
'Reconnect': 'Kết nối lại',
'Show Setting': 'Cài đặt',
'Hide Setting': 'Ẩn cài đặt',
'Screenshot': 'Chụp màn hình',
'Play Speed': 'Tốc độ phát',
'Aspect Ratio': 'Tỷ lệ khung hình',
'Default': 'Mặc định',
'Normal': 'Bình thường',
'Open': 'Mở',
'Switch Video': 'Chuyển video',
'Switch Subtitle': 'Chuyển phụ đề',
'Fullscreen': 'Toàn màn hình',
'Exit Fullscreen': 'Thoát toàn màn hình',
'Web Fullscreen': 'Toàn màn hình trình duyệt',
'Exit Web Fullscreen': 'Thoát toàn màn hình trình duyệt',
'Mini Player': 'Trình phát mini',
'PIP Mode': 'Phát trong hình',
'Exit PIP Mode': 'Thoát phát trong hình',
'PIP Not Supported': 'Không hỗ trợ phát trong hình',
'Fullscreen Not Supported': 'Không hỗ trợ toàn màn hình',
'Subtitle Offset': 'Độ trễ phụ đề',
'Last Seen': 'Lần xem cuối',
'Jump Play': 'Nhảy đến đoạn phát',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay không khả dụng',
}
export default vi;
export default vi
if (typeof window !== 'undefined') {
window['artplayer-i18n-vi'] = vi;
window['artplayer-i18n-vi'] = vi
}
+39 -39
View File
@@ -1,44 +1,44 @@
const zhCn = {
'Video Info': '统计信息',
Close: '关闭',
'Video Load Failed': '加载失败',
Volume: '音量',
Play: '播放',
Pause: '暂停',
Rate: '速度',
Mute: '静音',
'Video Flip': '画面翻转',
Horizontal: '水平',
Vertical: '垂直',
Reconnect: '重新连接',
'Show Setting': '显示设置',
'Hide Setting': '隐藏设置',
Screenshot: '截图',
'Play Speed': '播放速度',
'Aspect Ratio': '画面比例',
Default: '默认',
Normal: '正常',
Open: '打开',
'Switch Video': '切换',
'Switch Subtitle': '切换字幕',
Fullscreen: '全屏',
'Exit Fullscreen': '退出全屏',
'Web Fullscreen': '网页全屏',
'Exit Web Fullscreen': '退出网页全屏',
'Mini Player': '迷你播放器',
'PIP Mode': '开启画中画',
'Exit PIP Mode': '退出画中画',
'PIP Not Supported': '不支持画中画',
'Fullscreen Not Supported': '不支持全屏',
'Subtitle Offset': '字幕偏移',
'Last Seen': '上次看到',
'Jump Play': '跳转播放',
AirPlay: '隔空播放',
'AirPlay Not Available': '隔空播放不可用',
};
'Video Info': '统计信息',
'Close': '关闭',
'Video Load Failed': '加载失败',
'Volume': '音量',
'Play': '播放',
'Pause': '暂停',
'Rate': '速度',
'Mute': '静音',
'Video Flip': '画面翻转',
'Horizontal': '水平',
'Vertical': '垂直',
'Reconnect': '重新连接',
'Show Setting': '显示设置',
'Hide Setting': '隐藏设置',
'Screenshot': '截图',
'Play Speed': '播放速度',
'Aspect Ratio': '画面比例',
'Default': '默认',
'Normal': '正常',
'Open': '打开',
'Switch Video': '切换',
'Switch Subtitle': '切换字幕',
'Fullscreen': '全屏',
'Exit Fullscreen': '退出全屏',
'Web Fullscreen': '网页全屏',
'Exit Web Fullscreen': '退出网页全屏',
'Mini Player': '迷你播放器',
'PIP Mode': '开启画中画',
'Exit PIP Mode': '退出画中画',
'PIP Not Supported': '不支持画中画',
'Fullscreen Not Supported': '不支持全屏',
'Subtitle Offset': '字幕偏移',
'Last Seen': '上次看到',
'Jump Play': '跳转播放',
'AirPlay': '隔空播放',
'AirPlay Not Available': '隔空播放不可用',
}
export default zhCn;
export default zhCn
if (typeof window !== 'undefined') {
window['artplayer-i18n-zh-cn'] = zhCn;
window['artplayer-i18n-zh-cn'] = zhCn
}
+39 -39
View File
@@ -1,44 +1,44 @@
const zhTw = {
'Video Info': '統計訊息',
Close: '關閉',
'Video Load Failed': '載入失敗',
Volume: '音量',
Play: '播放',
Pause: '暫停',
Rate: '速度',
Mute: '靜音',
'Video Flip': '畫面翻轉',
Horizontal: '水平',
Vertical: '垂直',
Reconnect: '重新連接',
'Show Setting': '顯示设置',
'Hide Setting': '隱藏设置',
Screenshot: '截圖',
'Play Speed': '播放速度',
'Aspect Ratio': '畫面比例',
Default: '默認',
Normal: '正常',
Open: '打開',
'Switch Video': '切換',
'Switch Subtitle': '切換字幕',
Fullscreen: '全屏',
'Exit Fullscreen': '退出全屏',
'Web Fullscreen': '網頁全屏',
'Exit Web Fullscreen': '退出網頁全屏',
'Mini Player': '迷你播放器',
'PIP Mode': '開啟畫中畫',
'Exit PIP Mode': '退出畫中畫',
'PIP Not Supported': '不支持畫中畫',
'Fullscreen Not Supported': '不支持全屏',
'Subtitle Offset': '字幕偏移',
'Last Seen': '上次看到',
'Jump Play': '跳轉播放',
AirPlay: '隔空播放',
'AirPlay Not Available': '隔空播放不可用',
};
'Video Info': '統計訊息',
'Close': '關閉',
'Video Load Failed': '載入失敗',
'Volume': '音量',
'Play': '播放',
'Pause': '暫停',
'Rate': '速度',
'Mute': '靜音',
'Video Flip': '畫面翻轉',
'Horizontal': '水平',
'Vertical': '垂直',
'Reconnect': '重新連接',
'Show Setting': '顯示设置',
'Hide Setting': '隱藏设置',
'Screenshot': '截圖',
'Play Speed': '播放速度',
'Aspect Ratio': '畫面比例',
'Default': '默認',
'Normal': '正常',
'Open': '打開',
'Switch Video': '切換',
'Switch Subtitle': '切換字幕',
'Fullscreen': '全屏',
'Exit Fullscreen': '退出全屏',
'Web Fullscreen': '網頁全屏',
'Exit Web Fullscreen': '退出網頁全屏',
'Mini Player': '迷你播放器',
'PIP Mode': '開啟畫中畫',
'Exit PIP Mode': '退出畫中畫',
'PIP Not Supported': '不支持畫中畫',
'Fullscreen Not Supported': '不支持全屏',
'Subtitle Offset': '字幕偏移',
'Last Seen': '上次看到',
'Jump Play': '跳轉播放',
'AirPlay': '隔空播放',
'AirPlay Not Available': '隔空播放不可用',
}
export default zhTw;
export default zhTw
if (typeof window !== 'undefined') {
window['artplayer-i18n-zh-tw'] = zhTw;
window['artplayer-i18n-zh-tw'] = zhTw
}
+65 -65
View File
@@ -1,69 +1,69 @@
import { def, getIcon } from '../utils';
import loading from 'bundle-text:./loading.svg';
import state from 'bundle-text:./state.svg';
import check from 'bundle-text:./check.svg';
import play from 'bundle-text:./play.svg';
import pause from 'bundle-text:./pause.svg';
import volume from 'bundle-text:./volume.svg';
import volumeClose from 'bundle-text:./volume-close.svg';
import screenshot from 'bundle-text:./screenshot.svg';
import setting from 'bundle-text:./setting.svg';
import arrowLeft from 'bundle-text:./arrow-left.svg';
import arrowRight from 'bundle-text:./arrow-right.svg';
import playbackRate from 'bundle-text:./playback-rate.svg';
import aspectRatio from 'bundle-text:./aspect-ratio.svg';
import config from 'bundle-text:./config.svg';
import pip from 'bundle-text:./pip.svg';
import lock from 'bundle-text:./lock.svg';
import unlock from 'bundle-text:./unlock.svg';
import fullscreenOff from 'bundle-text:./fullscreen-off.svg';
import fullscreenOn from 'bundle-text:./fullscreen-on.svg';
import fullscreenWebOff from 'bundle-text:./fullscreen-web-off.svg';
import fullscreenWebOn from 'bundle-text:./fullscreen-web-on.svg';
import switchOn from 'bundle-text:./switch-on.svg';
import switchOff from 'bundle-text:./switch-off.svg';
import flip from 'bundle-text:./flip.svg';
import error from 'bundle-text:./error.svg';
import close from 'bundle-text:./close.svg';
import airplay from 'bundle-text:./airplay.svg';
import airplay from 'bundle-text:./airplay.svg'
import arrowLeft from 'bundle-text:./arrow-left.svg'
import arrowRight from 'bundle-text:./arrow-right.svg'
import aspectRatio from 'bundle-text:./aspect-ratio.svg'
import check from 'bundle-text:./check.svg'
import close from 'bundle-text:./close.svg'
import config from 'bundle-text:./config.svg'
import error from 'bundle-text:./error.svg'
import flip from 'bundle-text:./flip.svg'
import fullscreenOff from 'bundle-text:./fullscreen-off.svg'
import fullscreenOn from 'bundle-text:./fullscreen-on.svg'
import fullscreenWebOff from 'bundle-text:./fullscreen-web-off.svg'
import fullscreenWebOn from 'bundle-text:./fullscreen-web-on.svg'
import loading from 'bundle-text:./loading.svg'
import lock from 'bundle-text:./lock.svg'
import pause from 'bundle-text:./pause.svg'
import pip from 'bundle-text:./pip.svg'
import play from 'bundle-text:./play.svg'
import playbackRate from 'bundle-text:./playback-rate.svg'
import screenshot from 'bundle-text:./screenshot.svg'
import setting from 'bundle-text:./setting.svg'
import state from 'bundle-text:./state.svg'
import switchOff from 'bundle-text:./switch-off.svg'
import switchOn from 'bundle-text:./switch-on.svg'
import unlock from 'bundle-text:./unlock.svg'
import volumeClose from 'bundle-text:./volume-close.svg'
import volume from 'bundle-text:./volume.svg'
import { def, getIcon } from '../utils'
export default class Icons {
constructor(art) {
const icons = {
loading,
state,
play,
pause,
check,
volume,
volumeClose,
screenshot,
setting,
pip,
arrowLeft,
arrowRight,
playbackRate,
aspectRatio,
config,
lock,
flip,
unlock,
fullscreenOff,
fullscreenOn,
fullscreenWebOff,
fullscreenWebOn,
switchOn,
switchOff,
error,
close,
airplay,
...art.option.icons,
};
for (const key in icons) {
def(this, key, {
get: () => getIcon(key, icons[key]),
});
}
constructor(art) {
const icons = {
loading,
state,
play,
pause,
check,
volume,
volumeClose,
screenshot,
setting,
pip,
arrowLeft,
arrowRight,
playbackRate,
aspectRatio,
config,
lock,
flip,
unlock,
fullscreenOff,
fullscreenOn,
fullscreenWebOff,
fullscreenWebOn,
switchOn,
switchOff,
error,
close,
airplay,
...art.option.icons,
}
for (const key in icons) {
def(this, key, {
get: () => getIcon(key, icons[key]),
})
}
}
}
+228 -226
View File
@@ -1,258 +1,260 @@
import { version } from '../package.json';
import style from 'bundle-text:./style/index.less';
import validator from 'option-validator';
import Emitter from './utils/emitter';
import * as utils from './utils';
import scheme from './scheme';
import config from './config';
import Template from './template';
import I18n from './i18n';
import Player from './player';
import Control from './control';
import Contextmenu from './contextmenu';
import Info from './info';
import Subtitle from './subtitle';
import Events from './events';
import Hotkey from './hotkey';
import Layer from './layer';
import Loading from './loading';
import Notice from './notice';
import Mask from './mask';
import Icons from './icons';
import Setting from './setting';
import Storage from './storage';
import Plugins from './plugins';
import style from 'bundle-text:./style/index.less'
import validator from 'option-validator'
import { version } from '../package.json'
import config from './config'
import Contextmenu from './contextmenu'
import Control from './control'
import Events from './events'
import Hotkey from './hotkey'
import I18n from './i18n'
import Icons from './icons'
import Info from './info'
import Layer from './layer'
import Loading from './loading'
import Mask from './mask'
import Notice from './notice'
import Player from './player'
import Plugins from './plugins'
import scheme from './scheme'
import Setting from './setting'
import Storage from './storage'
import Subtitle from './subtitle'
import Template from './template'
import * as utils from './utils'
import Emitter from './utils/emitter'
let id = 0;
const instances = [];
let id = 0
const instances = []
export default class Artplayer extends Emitter {
constructor(option, readyCallback) {
super();
constructor(option, readyCallback) {
super()
this.id = ++id;
this.id = ++id
const mergeOption = utils.mergeDeep(Artplayer.option, option);
mergeOption.container = option.container;
const mergeOption = utils.mergeDeep(Artplayer.option, option)
mergeOption.container = option.container
this.option = validator(mergeOption, scheme);
this.option = validator(mergeOption, scheme)
this.isLock = false;
this.isReady = false;
this.isFocus = false;
this.isInput = false;
this.isRotate = false;
this.isDestroy = false;
this.isLock = false
this.isReady = false
this.isFocus = false
this.isInput = false
this.isRotate = false
this.isDestroy = false
this.template = new Template(this);
this.events = new Events(this);
this.storage = new Storage(this);
this.icons = new Icons(this);
this.i18n = new I18n(this);
this.notice = new Notice(this);
this.player = new Player(this);
this.layers = new Layer(this);
this.controls = new Control(this);
this.contextmenu = new Contextmenu(this);
this.subtitle = new Subtitle(this);
this.info = new Info(this);
this.loading = new Loading(this);
this.hotkey = new Hotkey(this);
this.mask = new Mask(this);
this.setting = new Setting(this);
this.plugins = new Plugins(this);
this.template = new Template(this)
this.events = new Events(this)
this.storage = new Storage(this)
this.icons = new Icons(this)
this.i18n = new I18n(this)
this.notice = new Notice(this)
this.player = new Player(this)
this.layers = new Layer(this)
this.controls = new Control(this)
this.contextmenu = new Contextmenu(this)
this.subtitle = new Subtitle(this)
this.info = new Info(this)
this.loading = new Loading(this)
this.hotkey = new Hotkey(this)
this.mask = new Mask(this)
this.setting = new Setting(this)
this.plugins = new Plugins(this)
if (typeof readyCallback === 'function') {
this.on('ready', () => readyCallback.call(this, this));
}
if (Artplayer.DEBUG) {
const log = (msg) => console.log(`[ART.${this.id}] -> ${msg}`);
log('Version@' + Artplayer.version);
for (let index = 0; index < config.events.length; index++) {
this.on('video:' + config.events[index], (event) => log('Event@' + event.type));
}
}
instances.push(this);
if (typeof readyCallback === 'function') {
this.on('ready', () => readyCallback.call(this, this))
}
static get instances() {
return instances;
if (Artplayer.DEBUG) {
// eslint-disable-next-line no-console
const log = msg => console.log(`[ART.${this.id}] -> ${msg}`)
log(`Version@${Artplayer.version}`)
for (let index = 0; index < config.events.length; index++) {
this.on(`video:${config.events[index]}`, event => log(`Event@${event.type}`))
}
}
static get version() {
return version;
}
instances.push(this)
}
static get config() {
return config;
}
static get instances() {
return instances
}
static get utils() {
return utils;
}
static get version() {
return version
}
static get scheme() {
return scheme;
}
static get config() {
return config
}
static get Emitter() {
return Emitter;
}
static get utils() {
return utils
}
static get validator() {
return validator;
}
static get scheme() {
return scheme
}
static get kindOf() {
return validator.kindOf;
}
static get Emitter() {
return Emitter
}
static get html() {
return Template.html;
}
static get validator() {
return validator
}
static get option() {
return {
id: '',
container: '#artplayer',
url: '',
poster: '',
type: '',
theme: '#f00',
volume: 0.7,
isLive: false,
muted: false,
autoplay: false,
autoSize: false,
autoMini: false,
loop: false,
flip: false,
playbackRate: false,
aspectRatio: false,
screenshot: false,
setting: false,
hotkey: true,
pip: false,
mutex: true,
backdrop: true,
fullscreen: false,
fullscreenWeb: false,
subtitleOffset: false,
miniProgressBar: false,
useSSR: false,
playsInline: true,
lock: false,
gesture: true,
fastForward: false,
autoPlayback: false,
autoOrientation: false,
airplay: false,
proxy: undefined,
layers: [],
contextmenu: [],
controls: [],
settings: [],
quality: [],
highlight: [],
plugins: [],
thumbnails: {
url: '',
number: 60,
column: 10,
width: 0,
height: 0,
scale: 1,
},
subtitle: {
url: '',
type: '',
style: {},
name: '',
escape: true,
encoding: 'utf-8',
onVttLoad: (vtt) => vtt,
},
moreVideoAttr: {
controls: false,
preload: utils.isSafari ? 'auto' : 'metadata',
},
i18n: {},
icons: {},
cssVar: {},
customType: {},
lang: navigator?.language.toLowerCase(),
};
}
static get kindOf() {
return validator.kindOf
}
get proxy() {
return this.events.proxy;
}
static get html() {
return Template.html
}
get query() {
return this.template.query;
static get option() {
return {
id: '',
container: '#artplayer',
url: '',
poster: '',
type: '',
theme: '#f00',
volume: 0.7,
isLive: false,
muted: false,
autoplay: false,
autoSize: false,
autoMini: false,
loop: false,
flip: false,
playbackRate: false,
aspectRatio: false,
screenshot: false,
setting: false,
hotkey: true,
pip: false,
mutex: true,
backdrop: true,
fullscreen: false,
fullscreenWeb: false,
subtitleOffset: false,
miniProgressBar: false,
useSSR: false,
playsInline: true,
lock: false,
gesture: true,
fastForward: false,
autoPlayback: false,
autoOrientation: false,
airplay: false,
proxy: undefined,
layers: [],
contextmenu: [],
controls: [],
settings: [],
quality: [],
highlight: [],
plugins: [],
thumbnails: {
url: '',
number: 60,
column: 10,
width: 0,
height: 0,
scale: 1,
},
subtitle: {
url: '',
type: '',
style: {},
name: '',
escape: true,
encoding: 'utf-8',
onVttLoad: vtt => vtt,
},
moreVideoAttr: {
controls: false,
preload: utils.isSafari ? 'auto' : 'metadata',
},
i18n: {},
icons: {},
cssVar: {},
customType: {},
lang: navigator?.language.toLowerCase(),
}
}
get video() {
return this.template.$video;
}
get proxy() {
return this.events.proxy
}
destroy(removeHtml = true) {
this.events.destroy();
this.template.destroy(removeHtml);
instances.splice(instances.indexOf(this), 1);
this.isDestroy = true;
this.emit('destroy');
}
get query() {
return this.template.query
}
get video() {
return this.template.$video
}
destroy(removeHtml = true) {
this.events.destroy()
this.template.destroy(removeHtml)
instances.splice(instances.indexOf(this), 1)
this.isDestroy = true
this.emit('destroy')
}
}
Artplayer.STYLE = style;
Artplayer.DEBUG = false;
Artplayer.CONTEXTMENU = true;
Artplayer.NOTICE_TIME = 2000;
Artplayer.SETTING_WIDTH = 250;
Artplayer.SETTING_ITEM_WIDTH = 200;
Artplayer.SETTING_ITEM_HEIGHT = 35;
Artplayer.RESIZE_TIME = 200;
Artplayer.SCROLL_TIME = 200;
Artplayer.SCROLL_GAP = 50;
Artplayer.AUTO_PLAYBACK_MAX = 10;
Artplayer.AUTO_PLAYBACK_MIN = 5;
Artplayer.AUTO_PLAYBACK_TIMEOUT = 3000;
Artplayer.RECONNECT_TIME_MAX = 5;
Artplayer.RECONNECT_SLEEP_TIME = 1000;
Artplayer.CONTROL_HIDE_TIME = 3000;
Artplayer.DBCLICK_TIME = 300;
Artplayer.DBCLICK_FULLSCREEN = true;
Artplayer.MOBILE_DBCLICK_PLAY = true;
Artplayer.MOBILE_CLICK_PLAY = false;
Artplayer.AUTO_ORIENTATION_TIME = 200;
Artplayer.INFO_LOOP_TIME = 1000;
Artplayer.FAST_FORWARD_VALUE = 3;
Artplayer.FAST_FORWARD_TIME = 1000;
Artplayer.TOUCH_MOVE_RATIO = 0.5;
Artplayer.VOLUME_STEP = 0.1;
Artplayer.SEEK_STEP = 5;
Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2];
Artplayer.ASPECT_RATIO = ['default', '4:3', '16:9'];
Artplayer.FLIP = ['normal', 'horizontal', 'vertical'];
Artplayer.FULLSCREEN_WEB_IN_BODY = false;
Artplayer.LOG_VERSION = true;
Artplayer.USE_RAF = false;
Artplayer.STYLE = style
Artplayer.DEBUG = false
Artplayer.CONTEXTMENU = true
Artplayer.NOTICE_TIME = 2000
Artplayer.SETTING_WIDTH = 250
Artplayer.SETTING_ITEM_WIDTH = 200
Artplayer.SETTING_ITEM_HEIGHT = 35
Artplayer.RESIZE_TIME = 200
Artplayer.SCROLL_TIME = 200
Artplayer.SCROLL_GAP = 50
Artplayer.AUTO_PLAYBACK_MAX = 10
Artplayer.AUTO_PLAYBACK_MIN = 5
Artplayer.AUTO_PLAYBACK_TIMEOUT = 3000
Artplayer.RECONNECT_TIME_MAX = 5
Artplayer.RECONNECT_SLEEP_TIME = 1000
Artplayer.CONTROL_HIDE_TIME = 3000
Artplayer.DBCLICK_TIME = 300
Artplayer.DBCLICK_FULLSCREEN = true
Artplayer.MOBILE_DBCLICK_PLAY = true
Artplayer.MOBILE_CLICK_PLAY = false
Artplayer.AUTO_ORIENTATION_TIME = 200
Artplayer.INFO_LOOP_TIME = 1000
Artplayer.FAST_FORWARD_VALUE = 3
Artplayer.FAST_FORWARD_TIME = 1000
Artplayer.TOUCH_MOVE_RATIO = 0.5
Artplayer.VOLUME_STEP = 0.1
Artplayer.SEEK_STEP = 5
Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2]
Artplayer.ASPECT_RATIO = ['default', '4:3', '16:9']
Artplayer.FLIP = ['normal', 'horizontal', 'vertical']
Artplayer.FULLSCREEN_WEB_IN_BODY = false
Artplayer.LOG_VERSION = true
Artplayer.USE_RAF = false
if (utils.isBrowser) {
window['Artplayer'] = Artplayer;
window.Artplayer = Artplayer
utils.setStyleText('artplayer-style', style);
utils.setStyleText('artplayer-style', style)
setTimeout(() => {
if (Artplayer.LOG_VERSION) {
console.log(
`%c ArtPlayer %c ${Artplayer.version} %c https://artplayer.org`,
'color: #fff; background: #5f5f5f',
'color: #fff; background: #4bc729',
'',
);
}
}, 100);
setTimeout(() => {
if (Artplayer.LOG_VERSION) {
// eslint-disable-next-line no-console
console.log(
`%c ArtPlayer %c ${Artplayer.version} %c https://artplayer.org`,
'color: #fff; background: #5f5f5f',
'color: #fff; background: #4bc729',
'',
)
}
}, 100)
}
+36 -36
View File
@@ -1,43 +1,43 @@
import { queryAll, isMobile } from './utils';
import Component from './utils/component';
import { isMobile, queryAll } from './utils'
import Component from './utils/component'
export default class Info extends Component {
constructor(art) {
super(art);
this.name = 'info';
constructor(art) {
super(art)
this.name = 'info'
if (!isMobile) {
this.init();
if (!isMobile) {
this.init()
}
}
init() {
const {
proxy,
constructor,
template: { $infoPanel, $infoClose, $video },
} = this.art
proxy($infoClose, 'click', () => {
this.show = false
})
let timer = null
const $types = queryAll('[data-video]', $infoPanel) || []
this.art.on('destroy', () => clearTimeout(timer))
function loop() {
for (let index = 0; index < $types.length; index++) {
const item = $types[index]
const value = $video[item.dataset.video]
const textContent = typeof value === 'number' ? value.toFixed(2) : value
if (item.textContent !== textContent) {
item.textContent = textContent
}
}
timer = setTimeout(loop, constructor.INFO_LOOP_TIME)
}
init() {
const {
proxy,
constructor,
template: { $infoPanel, $infoClose, $video },
} = this.art;
proxy($infoClose, 'click', () => {
this.show = false;
});
let timer = null;
const $types = queryAll('[data-video]', $infoPanel) || [];
this.art.on('destroy', () => clearTimeout(timer));
function loop() {
for (let index = 0; index < $types.length; index++) {
const item = $types[index];
const value = $video[item.dataset.video];
const innerText = typeof value === 'number' ? value.toFixed(2) : value;
if (item.innerText !== innerText) {
item.innerText = innerText;
}
}
timer = setTimeout(loop, constructor.INFO_LOOP_TIME);
}
loop();
}
loop()
}
}
+12 -12
View File
@@ -1,19 +1,19 @@
import Component from './utils/component';
import Component from './utils/component'
export default class Layer extends Component {
constructor(art) {
super(art);
constructor(art) {
super(art)
const {
option,
template: { $layer },
} = art;
const {
option,
template: { $layer },
} = art
this.name = 'layer';
this.$parent = $layer;
this.name = 'layer'
this.$parent = $layer
for (let index = 0; index < option.layers.length; index++) {
this.add(option.layers[index]);
}
for (let index = 0; index < option.layers.length; index++) {
this.add(option.layers[index])
}
}
}
+144 -144
View File
@@ -1,153 +1,153 @@
const methodMap = [
[
'requestFullscreen',
'exitFullscreen',
'fullscreenElement',
'fullscreenEnabled',
'fullscreenchange',
'fullscreenerror',
],
// New WebKit
[
'webkitRequestFullscreen',
'webkitExitFullscreen',
'webkitFullscreenElement',
'webkitFullscreenEnabled',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
// Old WebKit
[
'webkitRequestFullScreen',
'webkitCancelFullScreen',
'webkitCurrentFullScreenElement',
'webkitCancelFullScreen',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
[
'mozRequestFullScreen',
'mozCancelFullScreen',
'mozFullScreenElement',
'mozFullScreenEnabled',
'mozfullscreenchange',
'mozfullscreenerror',
],
[
'msRequestFullscreen',
'msExitFullscreen',
'msFullscreenElement',
'msFullscreenEnabled',
'MSFullscreenChange',
'MSFullscreenError',
],
];
[
'requestFullscreen',
'exitFullscreen',
'fullscreenElement',
'fullscreenEnabled',
'fullscreenchange',
'fullscreenerror',
],
// New WebKit
[
'webkitRequestFullscreen',
'webkitExitFullscreen',
'webkitFullscreenElement',
'webkitFullscreenEnabled',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
// Old WebKit
[
'webkitRequestFullScreen',
'webkitCancelFullScreen',
'webkitCurrentFullScreenElement',
'webkitCancelFullScreen',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
[
'mozRequestFullScreen',
'mozCancelFullScreen',
'mozFullScreenElement',
'mozFullScreenEnabled',
'mozfullscreenchange',
'mozfullscreenerror',
],
[
'msRequestFullscreen',
'msExitFullscreen',
'msFullscreenElement',
'msFullscreenEnabled',
'MSFullscreenChange',
'MSFullscreenError',
],
]
const nativeAPI = (() => {
if (typeof document === 'undefined') {
return false;
if (typeof document === 'undefined') {
return false
}
const unprefixedMethods = methodMap[0]
const returnValue = {}
for (const methodList of methodMap) {
const exitFullscreenMethod = methodList[1]
if (exitFullscreenMethod in document) {
for (const [index, method] of methodList.entries()) {
returnValue[unprefixedMethods[index]] = method
}
return returnValue
}
}
const unprefixedMethods = methodMap[0];
const returnValue = {};
for (const methodList of methodMap) {
const exitFullscreenMethod = methodList[1];
if (exitFullscreenMethod in document) {
for (const [index, method] of methodList.entries()) {
returnValue[unprefixedMethods[index]] = method;
}
return returnValue;
}
}
return false;
})();
return false
})()
const eventNameMap = {
change: nativeAPI.fullscreenchange,
error: nativeAPI.fullscreenerror,
};
let screenfull = {
request(element = document.documentElement, options) {
return new Promise((resolve, reject) => {
const onFullScreenEntered = () => {
screenfull.off('change', onFullScreenEntered);
resolve();
};
screenfull.on('change', onFullScreenEntered);
const returnPromise = element[nativeAPI.requestFullscreen](options);
if (returnPromise instanceof Promise) {
returnPromise.then(onFullScreenEntered).catch(reject);
}
});
},
exit() {
return new Promise((resolve, reject) => {
if (!screenfull.isFullscreen) {
resolve();
return;
}
const onFullScreenExit = () => {
screenfull.off('change', onFullScreenExit);
resolve();
};
screenfull.on('change', onFullScreenExit);
const returnPromise = document[nativeAPI.exitFullscreen]();
if (returnPromise instanceof Promise) {
returnPromise.then(onFullScreenExit).catch(reject);
}
});
},
toggle(element, options) {
return screenfull.isFullscreen ? screenfull.exit() : screenfull.request(element, options);
},
onchange(callback) {
screenfull.on('change', callback);
},
onerror(callback) {
screenfull.on('error', callback);
},
on(event, callback) {
const eventName = eventNameMap[event];
if (eventName) {
document.addEventListener(eventName, callback, false);
}
},
off(event, callback) {
const eventName = eventNameMap[event];
if (eventName) {
document.removeEventListener(eventName, callback, false);
}
},
raw: nativeAPI,
};
Object.defineProperties(screenfull, {
isFullscreen: {
get: () => Boolean(document[nativeAPI.fullscreenElement]),
},
element: {
enumerable: true,
get: () => document[nativeAPI.fullscreenElement],
},
isEnabled: {
enumerable: true,
get: () => Boolean(document[nativeAPI.fullscreenEnabled]),
},
});
if (!nativeAPI) {
screenfull = { isEnabled: false };
change: nativeAPI.fullscreenchange,
error: nativeAPI.fullscreenerror,
}
export default screenfull;
const screenfull = {
request(element = document.documentElement, options) {
return new Promise((resolve, reject) => {
const onFullScreenEntered = () => {
screenfull.off('change', onFullScreenEntered)
resolve()
}
screenfull.on('change', onFullScreenEntered)
const returnPromise = element[nativeAPI.requestFullscreen](options)
if (returnPromise instanceof Promise) {
returnPromise.then(onFullScreenEntered).catch(reject)
}
})
},
exit() {
return new Promise((resolve, reject) => {
if (!screenfull.isFullscreen) {
resolve()
return
}
const onFullScreenExit = () => {
screenfull.off('change', onFullScreenExit)
resolve()
}
screenfull.on('change', onFullScreenExit)
const returnPromise = document[nativeAPI.exitFullscreen]()
if (returnPromise instanceof Promise) {
returnPromise.then(onFullScreenExit).catch(reject)
}
})
},
toggle(element, options) {
return screenfull.isFullscreen ? screenfull.exit() : screenfull.request(element, options)
},
onchange(callback) {
screenfull.on('change', callback)
},
onerror(callback) {
screenfull.on('error', callback)
},
on(event, callback) {
const eventName = eventNameMap[event]
if (eventName) {
document.addEventListener(eventName, callback, false)
}
},
off(event, callback) {
const eventName = eventNameMap[event]
if (eventName) {
document.removeEventListener(eventName, callback, false)
}
},
raw: nativeAPI,
}
Object.defineProperties(screenfull, {
isFullscreen: {
get: () => Boolean(document[nativeAPI.fullscreenElement]),
},
element: {
enumerable: true,
get: () => document[nativeAPI.fullscreenElement],
},
isEnabled: {
enumerable: true,
get: () => Boolean(document[nativeAPI.fullscreenEnabled]),
},
})
if (!nativeAPI) {
screenfull.isEnabled = false
}
export default screenfull
+7 -7
View File
@@ -1,10 +1,10 @@
import { append } from './utils';
import Component from './utils/component';
import { append } from './utils'
import Component from './utils/component'
export default class Loading extends Component {
constructor(art) {
super(art);
this.name = 'loading';
append(art.template.$loading, art.icons.loading);
}
constructor(art) {
super(art)
this.name = 'loading'
append(art.template.$loading, art.icons.loading)
}
}
+15 -15
View File
@@ -1,23 +1,23 @@
import { append, setStyle } from './utils';
import Component from './utils/component';
import { append, setStyle } from './utils'
import Component from './utils/component'
export default class Mask extends Component {
constructor(art) {
super(art);
constructor(art) {
super(art)
this.name = 'mask';
const { template, icons, events } = art;
this.name = 'mask'
const { template, icons, events } = art
const $state = append(template.$state, icons.state);
const $error = append(template.$state, icons.error);
const $state = append(template.$state, icons.state)
const $error = append(template.$state, icons.error)
setStyle($error, 'display', 'none');
setStyle($error, 'display', 'none')
art.on('destroy', () => {
setStyle($state, 'display', 'none');
setStyle($error, 'display', null);
});
art.on('destroy', () => {
setStyle($state, 'display', 'none')
setStyle($error, 'display', null)
})
events.proxy(template.$state, 'click', () => art.play());
}
events.proxy(template.$state, 'click', () => art.play())
}
}
+29 -21
View File
@@ -1,27 +1,35 @@
import { addClass, removeClass } from './utils';
import { addClass, removeClass } from './utils'
export default class Notice {
constructor(art) {
this.art = art;
this.timer = null;
}
constructor(art) {
this.art = art
this.timer = null
}
set show(msg) {
const {
constructor,
template: { $player, $noticeInner },
} = this.art;
set show(msg) {
const {
constructor,
template: { $player, $noticeInner },
} = this.art
if (msg) {
$noticeInner.innerText = msg instanceof Error ? msg.message.trim() : msg;
addClass($player, 'art-notice-show');
clearTimeout(this.timer);
this.timer = setTimeout(() => {
$noticeInner.innerText = '';
removeClass($player, 'art-notice-show');
}, constructor.NOTICE_TIME);
} else {
removeClass($player, 'art-notice-show');
}
if (msg) {
$noticeInner.textContent = msg instanceof Error ? msg.message.trim() : msg
addClass($player, 'art-notice-show')
clearTimeout(this.timer)
this.timer = setTimeout(() => {
$noticeInner.textContent = ''
removeClass($player, 'art-notice-show')
}, constructor.NOTICE_TIME)
}
else {
removeClass($player, 'art-notice-show')
}
}
get show() {
const {
template: { $player },
} = this.art
return $player.classList.contains('art-notice-show')
}
}
+34 -32
View File
@@ -1,38 +1,40 @@
import { def } from '../utils';
import { def } from '../utils'
export default function airplayMix(art) {
const {
i18n,
notice,
proxy,
template: { $video },
} = art;
const {
i18n,
notice,
proxy,
template: { $video },
} = art
let available = true;
let available = true
if (window.WebKitPlaybackTargetAvailabilityEvent && $video.webkitShowPlaybackTargetPicker) {
proxy($video, 'webkitplaybacktargetavailabilitychanged', (event) => {
switch (event.availability) {
case 'available':
available = true;
break;
case 'not-available':
available = false;
break;
}
});
} else {
available = false;
}
if (window.WebKitPlaybackTargetAvailabilityEvent && $video.webkitShowPlaybackTargetPicker) {
proxy($video, 'webkitplaybacktargetavailabilitychanged', (event) => {
switch (event.availability) {
case 'available':
available = true
break
case 'not-available':
available = false
break
}
})
}
else {
available = false
}
def(art, 'airplay', {
value() {
if (available) {
$video.webkitShowPlaybackTargetPicker();
art.emit('airplay');
} else {
notice.show = i18n.get('AirPlay Not Available');
}
},
});
def(art, 'airplay', {
value() {
if (available) {
$video.webkitShowPlaybackTargetPicker()
art.emit('airplay')
}
else {
notice.show = i18n.get('AirPlay Not Available')
}
},
})
}
+40 -37
View File
@@ -1,44 +1,47 @@
import { setStyle, def } from '../utils';
import { def, setStyle } from '../utils'
export default function aspectRatioMix(art) {
const {
i18n,
notice,
template: { $video, $player },
} = art;
const {
i18n,
notice,
template: { $video, $player },
} = art
def(art, 'aspectRatio', {
get() {
return $player.dataset.aspectRatio || 'default';
},
set(ratio) {
if (!ratio) ratio = 'default';
if (ratio === 'default') {
setStyle($video, 'width', null);
setStyle($video, 'height', null);
setStyle($video, 'margin', null);
delete $player.dataset.aspectRatio;
} else {
const ratioArray = ratio.split(':').map(Number);
const { clientWidth, clientHeight } = $player;
const playerRatio = clientWidth / clientHeight;
const setupRatio = ratioArray[0] / ratioArray[1];
def(art, 'aspectRatio', {
get() {
return $player.dataset.aspectRatio || 'default'
},
set(ratio) {
if (!ratio)
ratio = 'default'
if (ratio === 'default') {
setStyle($video, 'width', null)
setStyle($video, 'height', null)
setStyle($video, 'margin', null)
delete $player.dataset.aspectRatio
}
else {
const ratioArray = ratio.split(':').map(Number)
const { clientWidth, clientHeight } = $player
const playerRatio = clientWidth / clientHeight
const setupRatio = ratioArray[0] / ratioArray[1]
if (playerRatio > setupRatio) {
setStyle($video, 'width', `${setupRatio * clientHeight}px`);
setStyle($video, 'height', '100%');
setStyle($video, 'margin', '0 auto');
} else {
setStyle($video, 'width', '100%');
setStyle($video, 'height', `${clientWidth / setupRatio}px`);
setStyle($video, 'margin', 'auto 0');
}
if (playerRatio > setupRatio) {
setStyle($video, 'width', `${setupRatio * clientHeight}px`)
setStyle($video, 'height', '100%')
setStyle($video, 'margin', '0 auto')
}
else {
setStyle($video, 'width', '100%')
setStyle($video, 'height', `${clientWidth / setupRatio}px`)
setStyle($video, 'margin', 'auto 0')
}
$player.dataset.aspectRatio = ratio;
}
$player.dataset.aspectRatio = ratio
}
notice.show = `${i18n.get('Aspect Ratio')}: ${ratio === 'default' ? i18n.get('Default') : ratio}`;
art.emit('aspectRatio', ratio);
},
});
notice.show = `${i18n.get('Aspect Ratio')}: ${ratio === 'default' ? i18n.get('Default') : ratio}`
art.emit('aspectRatio', ratio)
},
})
}
+11 -10
View File
@@ -1,14 +1,15 @@
import { def } from '../utils';
import { def } from '../utils'
export default function attrMix(art) {
const {
template: { $video },
} = art;
const {
template: { $video },
} = art
def(art, 'attr', {
value(key, value) {
if (value === undefined) return $video[key];
$video[key] = value;
},
});
def(art, 'attr', {
value(key, value) {
if (value === undefined)
return $video[key]
$video[key] = value
},
})
}
+13 -13
View File
@@ -1,17 +1,17 @@
import { setStyle, def } from '../utils';
import { def, setStyle } from '../utils'
export default function autoHeightMix(art) {
const {
template: { $container, $video },
} = art;
const {
template: { $container, $video },
} = art
def(art, 'autoHeight', {
value() {
const { clientWidth } = $container;
const { videoHeight, videoWidth } = $video;
const height = videoHeight * (clientWidth / videoWidth);
setStyle($container, 'height', height + 'px');
art.emit('autoHeight', height);
},
});
def(art, 'autoHeight', {
value() {
const { clientWidth } = $container
const { videoHeight, videoWidth } = $video
const height = videoHeight * (clientWidth / videoWidth)
setStyle($container, 'height', `${height}px`)
art.emit('autoHeight', height)
},
})
}
+24 -23
View File
@@ -1,27 +1,28 @@
import { setStyle, def, getRect } from '../utils';
import { def, getRect, setStyle } from '../utils'
export default function autoSizeMix(art) {
const { $container, $player, $video } = art.template;
const { $container, $player, $video } = art.template
def(art, 'autoSize', {
value() {
const { videoWidth, videoHeight } = $video;
const { width, height } = getRect($container);
const videoRatio = videoWidth / videoHeight;
const containerRatio = width / height;
if (containerRatio > videoRatio) {
const percentage = ((height * videoRatio) / width) * 100;
setStyle($player, 'width', `${percentage}%`);
setStyle($player, 'height', '100%');
} else {
const percentage = (width / videoRatio / height) * 100;
setStyle($player, 'width', '100%');
setStyle($player, 'height', `${percentage}%`);
}
art.emit('autoSize', {
width: art.width,
height: art.height,
});
},
});
def(art, 'autoSize', {
value() {
const { videoWidth, videoHeight } = $video
const { width, height } = getRect($container)
const videoRatio = videoWidth / videoHeight
const containerRatio = width / height
if (containerRatio > videoRatio) {
const percentage = ((height * videoRatio) / width) * 100
setStyle($player, 'width', `${percentage}%`)
setStyle($player, 'height', '100%')
}
else {
const percentage = (width / videoRatio / height) * 100
setStyle($player, 'width', '100%')
setStyle($player, 'height', `${percentage}%`)
}
art.emit('autoSize', {
width: art.width,
height: art.height,
})
},
})
}
+12 -11
View File
@@ -1,15 +1,16 @@
import { def } from '../utils';
import { def } from '../utils'
export default function cssVarMix(art) {
const { $player } = art.template;
const { $player } = art.template
def(art, 'cssVar', {
value(key, value) {
if (value) {
return $player.style.setProperty(key, value);
} else {
return getComputedStyle($player).getPropertyValue(key);
}
},
});
def(art, 'cssVar', {
value(key, value) {
if (value) {
return $player.style.setProperty(key, value)
}
else {
return getComputedStyle($player).getPropertyValue(key)
}
},
})
}
+11 -10
View File
@@ -1,14 +1,15 @@
import { clamp, def } from '../utils';
import { clamp, def } from '../utils'
export default function currentTimeMix(art) {
const { $video } = art.template;
const { $video } = art.template
def(art, 'currentTime', {
get: () => $video.currentTime || 0,
set: (time) => {
time = parseFloat(time);
if (Number.isNaN(time)) return;
$video.currentTime = clamp(time, 0, art.duration);
},
});
def(art, 'currentTime', {
get: () => $video.currentTime || 0,
set: (time) => {
time = Number.parseFloat(time)
if (Number.isNaN(time))
return
$video.currentTime = clamp(time, 0, art.duration)
},
})
}
+9 -8
View File
@@ -1,11 +1,12 @@
import { def } from '../utils';
import { def } from '../utils'
export default function durationMix(art) {
def(art, 'duration', {
get: () => {
const { duration } = art.template.$video;
if (duration === Infinity) return 0;
return duration || 0;
},
});
def(art, 'duration', {
get: () => {
const { duration } = art.template.$video
if (duration === Infinity)
return 0
return duration || 0
},
})
}
+128 -126
View File
@@ -1,153 +1,155 @@
import config from '../config';
import { sleep, addClass, setStyle, isMobile } from '../utils';
import config from '../config'
import { addClass, isMobile, setStyle, sleep } from '../utils'
export default function eventInit(art) {
const {
i18n,
notice,
option,
constructor,
proxy,
template: { $player, $video, $poster },
} = art;
const {
i18n,
notice,
option,
constructor,
proxy,
template: { $player, $video, $poster },
} = art
let reconnectTime = 0;
let reconnectTime = 0
for (let index = 0; index < config.events.length; index++) {
proxy($video, config.events[index], (event) => {
art.emit(`video:${event.type}`, event);
});
for (let index = 0; index < config.events.length; index++) {
proxy($video, config.events[index], (event) => {
art.emit(`video:${event.type}`, event)
})
}
// art.on('video:abort', () => {
// });
art.on('video:canplay', () => {
reconnectTime = 0
art.loading.show = false
})
art.once('video:canplay', () => {
art.loading.show = false
art.controls.show = true
art.mask.show = true
art.isReady = true
art.emit('ready')
})
// art.on('video:canplaythrough', () => {
// });
// art.on('video:durationchange', () => {
// });
// art.on('video:emptied', () => {
// });
art.on('video:ended', () => {
if (option.loop) {
art.seek = 0
art.play()
art.controls.show = false
art.mask.show = false
}
else {
art.controls.show = true
art.mask.show = true
}
})
// art.on('video:abort', () => {
art.on('video:error', async (error) => {
if (reconnectTime < constructor.RECONNECT_TIME_MAX) {
await sleep(constructor.RECONNECT_SLEEP_TIME)
reconnectTime += 1
art.url = option.url
notice.show = `${i18n.get('Reconnect')}: ${reconnectTime}`
art.emit('error', error, reconnectTime)
}
else {
art.mask.show = true
art.loading.show = false
art.controls.show = true
addClass($player, 'art-error')
await sleep(constructor.RECONNECT_SLEEP_TIME)
notice.show = i18n.get('Video Load Failed')
}
})
// });
// art.on('video:loadeddata', () => {
art.on('video:canplay', () => {
reconnectTime = 0;
art.loading.show = false;
});
// });
art.once('video:canplay', () => {
art.loading.show = false;
art.controls.show = true;
art.mask.show = true;
art.isReady = true;
art.emit('ready');
});
art.on('video:loadedmetadata', () => {
art.emit('resize')
if (isMobile) {
art.loading.show = false
art.controls.show = true
art.mask.show = true
}
})
// art.on('video:canplaythrough', () => {
art.on('video:loadstart', () => {
art.loading.show = true
art.mask.show = false
art.controls.show = true
})
// });
art.on('video:pause', () => {
art.controls.show = true
art.mask.show = true
})
// art.on('video:durationchange', () => {
art.on('video:play', () => {
art.mask.show = false
setStyle($poster, 'display', 'none')
})
// });
art.on('video:playing', () => {
art.mask.show = false
})
// art.on('video:emptied', () => {
art.on('video:progress', () => {
if (art.playing) {
art.loading.show = false
}
})
// });
// art.on('video:ratechange', () => {
art.on('video:ended', () => {
if (option.loop) {
art.seek = 0;
art.play();
art.controls.show = false;
art.mask.show = false;
} else {
art.controls.show = true;
art.mask.show = true;
}
});
// });
art.on('video:error', async (error) => {
if (reconnectTime < constructor.RECONNECT_TIME_MAX) {
await sleep(constructor.RECONNECT_SLEEP_TIME);
reconnectTime += 1;
art.url = option.url;
notice.show = `${i18n.get('Reconnect')}: ${reconnectTime}`;
art.emit('error', error, reconnectTime);
} else {
art.mask.show = true;
art.loading.show = false;
art.controls.show = true;
addClass($player, 'art-error');
await sleep(constructor.RECONNECT_SLEEP_TIME);
notice.show = i18n.get('Video Load Failed');
}
});
art.on('video:seeked', () => {
art.loading.show = false
art.mask.show = true
})
// art.on('video:loadeddata', () => {
art.on('video:seeking', () => {
art.loading.show = true
art.mask.show = false
})
// });
// art.on('video:stalled', () => {
art.on('video:loadedmetadata', () => {
art.emit('resize');
if (isMobile) {
art.loading.show = false;
art.controls.show = true;
art.mask.show = true;
}
});
// });
art.on('video:loadstart', () => {
art.loading.show = true;
art.mask.show = false;
art.controls.show = true;
});
// art.on('video:suspend', () => {
art.on('video:pause', () => {
art.controls.show = true;
art.mask.show = true;
});
// });
art.on('video:play', () => {
art.mask.show = false;
setStyle($poster, 'display', 'none');
});
art.on('video:timeupdate', () => {
art.mask.show = false
})
art.on('video:playing', () => {
art.mask.show = false;
});
// art.on('video:volumechange', () => {
art.on('video:progress', () => {
if (art.playing) {
art.loading.show = false;
}
});
// });
// art.on('video:ratechange', () => {
// });
art.on('video:seeked', () => {
art.loading.show = false;
art.mask.show = true;
});
art.on('video:seeking', () => {
art.loading.show = true;
art.mask.show = false;
});
// art.on('video:stalled', () => {
// });
// art.on('video:suspend', () => {
// });
art.on('video:timeupdate', () => {
art.mask.show = false;
});
// art.on('video:volumechange', () => {
// });
art.on('video:waiting', () => {
art.loading.show = true;
art.mask.show = false;
});
art.on('video:waiting', () => {
art.loading.show = true
art.mask.show = false
})
}
+23 -21
View File
@@ -1,26 +1,28 @@
import { def, capitalize } from '../utils';
import { capitalize, def } from '../utils'
export default function flipMix(art) {
const {
template: { $player },
i18n,
notice,
} = art;
const {
template: { $player },
i18n,
notice,
} = art
def(art, 'flip', {
get() {
return $player.dataset.flip || 'normal';
},
set(flip) {
if (!flip) flip = 'normal';
if (flip === 'normal') {
delete $player.dataset.flip;
} else {
$player.dataset.flip = flip;
}
def(art, 'flip', {
get() {
return $player.dataset.flip || 'normal'
},
set(flip) {
if (!flip)
flip = 'normal'
if (flip === 'normal') {
delete $player.dataset.flip
}
else {
$player.dataset.flip = flip
}
notice.show = `${i18n.get('Video Flip')}: ${i18n.get(capitalize(flip))}`;
art.emit('flip', flip);
},
});
notice.show = `${i18n.get('Video Flip')}: ${i18n.get(capitalize(flip))}`
art.emit('flip', flip)
},
})
}
+78 -73
View File
@@ -1,83 +1,88 @@
import screenfull from '../libs/screenfull';
import { addClass, removeClass, def, get } from '../utils';
import screenfull from '../libs/screenfull'
import { addClass, def, get, removeClass } from '../utils'
export default function fullscreenMix(art) {
const {
i18n,
notice,
template: { $video, $player },
} = art;
const {
i18n,
notice,
template: { $video, $player },
} = art
const nativeScreenfull = (art) => {
screenfull.on('change', () => {
art.emit('fullscreen', screenfull.isFullscreen);
const nativeScreenfull = (art) => {
screenfull.on('change', () => {
art.emit('fullscreen', screenfull.isFullscreen)
if (screenfull.isFullscreen) {
art.state = 'fullscreen';
addClass($player, 'art-fullscreen');
} else {
removeClass($player, 'art-fullscreen');
}
if (screenfull.isFullscreen) {
art.state = 'fullscreen'
addClass($player, 'art-fullscreen')
}
else {
removeClass($player, 'art-fullscreen')
}
art.emit('resize');
});
art.emit('resize')
})
screenfull.on('error', (event) => {
art.emit('fullscreenError', event);
});
screenfull.on('error', (event) => {
art.emit('fullscreenError', event)
})
def(art, 'fullscreen', {
get() {
return screenfull.isFullscreen;
},
async set(value) {
if (value) {
await screenfull.request($player);
} else {
await screenfull.exit();
}
},
});
};
const webkitScreenfull = (art) => {
art.proxy(document, 'webkitfullscreenchange', () => {
art.emit('fullscreen', art.fullscreen);
art.emit('resize');
});
def(art, 'fullscreen', {
get() {
return document.fullscreenElement === $video;
},
set(value) {
if (value) {
art.state = 'fullscreen';
$video.webkitEnterFullscreen();
} else {
$video.webkitExitFullscreen();
}
},
});
};
art.once('video:loadedmetadata', () => {
if (screenfull.isEnabled) {
nativeScreenfull(art);
} else if ($video.webkitSupportsFullscreen) {
webkitScreenfull(art);
} else {
def(art, 'fullscreen', {
get() {
return false;
},
set() {
notice.show = i18n.get('Fullscreen Not Supported');
},
});
def(art, 'fullscreen', {
get() {
return screenfull.isFullscreen
},
async set(value) {
if (value) {
await screenfull.request($player)
}
else {
await screenfull.exit()
}
},
})
}
// Asynchronous setting
def(art, 'fullscreen', get(art, 'fullscreen'));
});
const webkitScreenfull = (art) => {
art.proxy(document, 'webkitfullscreenchange', () => {
art.emit('fullscreen', art.fullscreen)
art.emit('resize')
})
def(art, 'fullscreen', {
get() {
return document.fullscreenElement === $video
},
set(value) {
if (value) {
art.state = 'fullscreen'
$video.webkitEnterFullscreen()
}
else {
$video.webkitExitFullscreen()
}
},
})
}
art.once('video:loadedmetadata', () => {
if (screenfull.isEnabled) {
nativeScreenfull(art)
}
else if ($video.webkitSupportsFullscreen) {
webkitScreenfull(art)
}
else {
def(art, 'fullscreen', {
get() {
return false
},
set() {
notice.show = i18n.get('Fullscreen Not Supported')
},
})
}
// Asynchronous setting
def(art, 'fullscreen', get(art, 'fullscreen'))
})
}
@@ -1,40 +1,41 @@
import { addClass, removeClass, hasClass, def, append, setStyle } from '../utils';
import { addClass, append, def, hasClass, removeClass, setStyle } from '../utils'
export default function fullscreenWebMix(art) {
const {
constructor,
template: { $container, $player },
} = art;
const {
constructor,
template: { $container, $player },
} = art
let cssText = '';
def(art, 'fullscreenWeb', {
get() {
return hasClass($player, 'art-fullscreen-web');
},
set(value) {
if (value) {
cssText = $player.style.cssText;
if (constructor.FULLSCREEN_WEB_IN_BODY) {
append(document.body, $player);
}
art.state = 'fullscreenWeb';
setStyle($player, 'width', '100%');
setStyle($player, 'height', '100%');
addClass($player, 'art-fullscreen-web');
art.emit('fullscreenWeb', true);
} else {
if (constructor.FULLSCREEN_WEB_IN_BODY) {
append($container, $player);
}
if (cssText) {
$player.style.cssText = cssText;
cssText = '';
}
removeClass($player, 'art-fullscreen-web');
art.emit('fullscreenWeb', false);
}
let cssText = ''
def(art, 'fullscreenWeb', {
get() {
return hasClass($player, 'art-fullscreen-web')
},
set(value) {
if (value) {
cssText = $player.style.cssText
if (constructor.FULLSCREEN_WEB_IN_BODY) {
append(document.body, $player)
}
art.state = 'fullscreenWeb'
setStyle($player, 'width', '100%')
setStyle($player, 'height', '100%')
addClass($player, 'art-fullscreen-web')
art.emit('fullscreenWeb', true)
}
else {
if (constructor.FULLSCREEN_WEB_IN_BODY) {
append($container, $player)
}
if (cssText) {
$player.style.cssText = cssText
cssText = ''
}
removeClass($player, 'art-fullscreen-web')
art.emit('fullscreenWeb', false)
}
art.emit('resize');
},
});
art.emit('resize')
},
})
}
+72 -72
View File
@@ -1,75 +1,75 @@
import urlMix from './urlMix';
import attrMix from './attrMix';
import playMix from './playMix';
import pauseMix from './pauseMix';
import toggleMix from './toggleMix';
import seekMix from './seekMix';
import volumeMix from './volumeMix';
import currentTimeMix from './currentTimeMix';
import durationMix from './durationMix';
import switchMix from './switchMix';
import playbackRateMix from './playbackRateMix';
import aspectRatioMix from './aspectRatioMix';
import screenshotMix from './screenshotMix';
import fullscreenMix from './fullscreenMix';
import fullscreenWebMix from './fullscreenWebMix';
import pipMix from './pipMix';
import loadedMix from './loadedMix';
import playedMix from './playedMix';
import playingMix from './playingMix';
import autoSizeMix from './autoSizeMix';
import rectMix from './rectMix';
import flipMix from './flipMix';
import miniMix from './miniMix';
import posterMix from './posterMix';
import autoHeightMix from './autoHeightMix';
import cssVarMix from './cssVarMix';
import themeMix from './themeMix';
import typeMix from './typeMix';
import stateMix from './stateMix';
import subtitleOffsetMix from './subtitleOffsetMix';
import airplayMix from './airplayMix';
import qualityMix from './qualityMix';
import thumbnailsMix from './thumbnailsMix';
import optionInit from './optionInit';
import eventInit from './eventInit';
import airplayMix from './airplayMix'
import aspectRatioMix from './aspectRatioMix'
import attrMix from './attrMix'
import autoHeightMix from './autoHeightMix'
import autoSizeMix from './autoSizeMix'
import cssVarMix from './cssVarMix'
import currentTimeMix from './currentTimeMix'
import durationMix from './durationMix'
import eventInit from './eventInit'
import flipMix from './flipMix'
import fullscreenMix from './fullscreenMix'
import fullscreenWebMix from './fullscreenWebMix'
import loadedMix from './loadedMix'
import miniMix from './miniMix'
import optionInit from './optionInit'
import pauseMix from './pauseMix'
import pipMix from './pipMix'
import playbackRateMix from './playbackRateMix'
import playedMix from './playedMix'
import playingMix from './playingMix'
import playMix from './playMix'
import posterMix from './posterMix'
import qualityMix from './qualityMix'
import rectMix from './rectMix'
import screenshotMix from './screenshotMix'
import seekMix from './seekMix'
import stateMix from './stateMix'
import subtitleOffsetMix from './subtitleOffsetMix'
import switchMix from './switchMix'
import themeMix from './themeMix'
import thumbnailsMix from './thumbnailsMix'
import toggleMix from './toggleMix'
import typeMix from './typeMix'
import urlMix from './urlMix'
import volumeMix from './volumeMix'
export default class Player {
constructor(art) {
urlMix(art);
attrMix(art);
playMix(art);
pauseMix(art);
toggleMix(art);
seekMix(art);
volumeMix(art);
currentTimeMix(art);
durationMix(art);
switchMix(art);
playbackRateMix(art);
aspectRatioMix(art);
screenshotMix(art);
fullscreenMix(art);
fullscreenWebMix(art);
pipMix(art);
loadedMix(art);
playedMix(art);
playingMix(art);
autoSizeMix(art);
rectMix(art);
flipMix(art);
miniMix(art);
posterMix(art);
autoHeightMix(art);
cssVarMix(art);
themeMix(art);
typeMix(art);
stateMix(art);
subtitleOffsetMix(art);
airplayMix(art);
qualityMix(art);
thumbnailsMix(art);
eventInit(art);
optionInit(art);
}
constructor(art) {
urlMix(art)
attrMix(art)
playMix(art)
pauseMix(art)
toggleMix(art)
seekMix(art)
volumeMix(art)
currentTimeMix(art)
durationMix(art)
switchMix(art)
playbackRateMix(art)
aspectRatioMix(art)
screenshotMix(art)
fullscreenMix(art)
fullscreenWebMix(art)
pipMix(art)
loadedMix(art)
playedMix(art)
playingMix(art)
autoSizeMix(art)
rectMix(art)
flipMix(art)
miniMix(art)
posterMix(art)
autoHeightMix(art)
cssVarMix(art)
themeMix(art)
typeMix(art)
stateMix(art)
subtitleOffsetMix(art)
airplayMix(art)
qualityMix(art)
thumbnailsMix(art)
eventInit(art)
optionInit(art)
}
}
+10 -10
View File
@@ -1,15 +1,15 @@
import { def } from '../utils';
import { def } from '../utils'
export default function loadedMix(art) {
const { $video } = art.template;
const { $video } = art.template
def(art, 'loaded', {
get: () => art.loadedTime / $video.duration,
});
def(art, 'loaded', {
get: () => art.loadedTime / $video.duration,
})
def(art, 'loadedTime', {
get: () => {
return $video.buffered.length ? $video.buffered.end($video.buffered.length - 1) : 0;
},
});
def(art, 'loadedTime', {
get: () => {
return $video.buffered.length ? $video.buffered.end($video.buffered.length - 1) : 0
},
})
}
+122 -118
View File
@@ -1,130 +1,134 @@
import { append, setStyle, addClass, removeClass, hasClass, def, isInViewport, createElement, getRect } from '../utils';
import { addClass, append, createElement, def, getRect, hasClass, isInViewport, removeClass, setStyle } from '../utils'
export default function miniMix(art) {
const {
icons,
proxy,
storage,
template: { $player, $video },
} = art;
const {
icons,
proxy,
storage,
template: { $player, $video },
} = art
let isDragging = false;
let lastPageX = 0;
let lastPageY = 0;
let isDragging = false
let lastPageX = 0
let lastPageY = 0
function hideMini() {
const { $mini } = art.template;
if ($mini) {
removeClass($player, 'art-mini');
setStyle($mini, 'display', 'none');
$player.prepend($video);
art.emit('mini', false);
function hideMini() {
const { $mini } = art.template
if ($mini) {
removeClass($player, 'art-mini')
setStyle($mini, 'display', 'none')
$player.prepend($video)
art.emit('mini', false)
}
}
function initState($play, $pause) {
if (art.playing) {
setStyle($play, 'display', `none`)
setStyle($pause, 'display', `flex`)
}
else {
setStyle($play, 'display', `flex`)
setStyle($pause, 'display', `none`)
}
}
function createMini() {
const { $mini } = art.template
if ($mini) {
append($mini, $video)
return setStyle($mini, 'display', 'flex')
}
else {
const $mini = createElement('div')
addClass($mini, 'art-mini-popup')
append(document.body, $mini)
art.template.$mini = $mini
append($mini, $video)
const $close = append($mini, `<div class="art-mini-close"></div>`)
append($close, icons.close)
proxy($close, 'click', hideMini)
const $state = append($mini, `<div class="art-mini-state"></div>`)
const $play = append($state, icons.play)
const $pause = append($state, icons.pause)
proxy($play, 'click', () => art.play())
proxy($pause, 'click', () => art.pause())
initState($play, $pause)
art.on('video:playing', () => initState($play, $pause))
art.on('video:pause', () => initState($play, $pause))
art.on('video:timeupdate', () => initState($play, $pause))
proxy($mini, 'mousedown', (event) => {
isDragging = event.button === 0
lastPageX = event.pageX
lastPageY = event.pageY
})
art.on('document:mousemove', (event) => {
if (isDragging) {
addClass($mini, 'art-mini-dragging')
const x = event.pageX - lastPageX
const y = event.pageY - lastPageY
setStyle($mini, 'transform', `translate(${x}px, ${y}px)`)
}
}
})
function initState($play, $pause) {
if (art.playing) {
setStyle($play, 'display', `none`);
setStyle($pause, 'display', `flex`);
} else {
setStyle($play, 'display', `flex`);
setStyle($pause, 'display', `none`);
art.on('document:mouseup', () => {
if (isDragging) {
isDragging = false
removeClass($mini, 'art-mini-dragging')
const rect = getRect($mini)
storage.set('left', rect.left)
storage.set('top', rect.top)
setStyle($mini, 'left', `${rect.left}px`)
setStyle($mini, 'top', `${rect.top}px`)
setStyle($mini, 'transform', null)
}
})
return $mini
}
}
function createMini() {
const { $mini } = art.template;
if ($mini) {
append($mini, $video);
return setStyle($mini, 'display', 'flex');
} else {
const $mini = createElement('div');
addClass($mini, 'art-mini-popup');
append(document.body, $mini);
art.template.$mini = $mini;
append($mini, $video);
function initMini() {
const { $mini } = art.template
const rect = getRect($mini)
const top = window.innerHeight - rect.height - 50
const left = window.innerWidth - rect.width - 50
storage.set('top', top)
storage.set('left', left)
setStyle($mini, 'top', `${top}px`)
setStyle($mini, 'left', `${left}px`)
}
const $close = append($mini, `<div class="art-mini-close"></div>`);
append($close, icons.close);
proxy($close, 'click', hideMini);
const $state = append($mini, `<div class="art-mini-state"></div>`);
const $play = append($state, icons.play);
const $pause = append($state, icons.pause);
proxy($play, 'click', () => art.play());
proxy($pause, 'click', () => art.pause());
initState($play, $pause);
art.on('video:playing', () => initState($play, $pause));
art.on('video:pause', () => initState($play, $pause));
art.on('video:timeupdate', () => initState($play, $pause));
proxy($mini, 'mousedown', (event) => {
isDragging = event.button === 0;
lastPageX = event.pageX;
lastPageY = event.pageY;
});
art.on('document:mousemove', (event) => {
if (isDragging) {
addClass($mini, 'art-mini-dragging');
const x = event.pageX - lastPageX;
const y = event.pageY - lastPageY;
setStyle($mini, 'transform', `translate(${x}px, ${y}px)`);
}
});
art.on('document:mouseup', () => {
if (isDragging) {
isDragging = false;
removeClass($mini, 'art-mini-dragging');
const rect = getRect($mini);
storage.set('left', rect.left);
storage.set('top', rect.top);
setStyle($mini, 'left', `${rect.left}px`);
setStyle($mini, 'top', `${rect.top}px`);
setStyle($mini, 'transform', null);
}
});
return $mini;
def(art, 'mini', {
get() {
return hasClass($player, 'art-mini')
},
set(value) {
if (value) {
art.state = 'mini'
addClass($player, 'art-mini')
const $mini = createMini()
const top = storage.get('top')
const left = storage.get('left')
if (typeof top === 'number' && typeof left === 'number') {
setStyle($mini, 'top', `${top}px`)
setStyle($mini, 'left', `${left}px`)
if (!isInViewport($mini)) {
initMini()
}
}
}
function initMini() {
const { $mini } = art.template;
const rect = getRect($mini);
const top = window.innerHeight - rect.height - 50;
const left = window.innerWidth - rect.width - 50;
storage.set('top', top);
storage.set('left', left);
setStyle($mini, 'top', `${top}px`);
setStyle($mini, 'left', `${left}px`);
}
def(art, 'mini', {
get() {
return hasClass($player, 'art-mini');
},
set(value) {
if (value) {
art.state = 'mini';
addClass($player, 'art-mini');
const $mini = createMini();
const top = storage.get('top');
const left = storage.get('left');
if (typeof top === 'number' && typeof left === 'number') {
setStyle($mini, 'top', `${top}px`);
setStyle($mini, 'left', `${left}px`);
if (!isInViewport($mini)) {
initMini();
}
} else {
initMini();
}
art.emit('mini', true);
} else {
hideMini();
}
},
});
else {
initMini()
}
art.emit('mini', true)
}
else {
hideMini()
}
},
})
}
+36 -36
View File
@@ -1,49 +1,49 @@
import { clamp, setStyle } from '../utils';
import { clamp, setStyle } from '../utils'
export default function optionInit(art) {
const {
option,
storage,
template: { $video, $poster },
} = art;
const {
option,
storage,
template: { $video, $poster },
} = art
for (const key in option.moreVideoAttr) {
art.attr(key, option.moreVideoAttr[key]);
}
for (const key in option.moreVideoAttr) {
art.attr(key, option.moreVideoAttr[key])
}
if (option.muted) {
art.muted = option.muted;
}
if (option.muted) {
art.muted = option.muted
}
if (option.volume) {
$video.volume = clamp(option.volume, 0, 1);
}
if (option.volume) {
$video.volume = clamp(option.volume, 0, 1)
}
const volumeStorage = storage.get('volume');
if (typeof volumeStorage === 'number') {
$video.volume = clamp(volumeStorage, 0, 1);
}
const volumeStorage = storage.get('volume')
if (typeof volumeStorage === 'number') {
$video.volume = clamp(volumeStorage, 0, 1)
}
if (option.poster) {
setStyle($poster, 'backgroundImage', `url(${option.poster})`);
}
if (option.poster) {
setStyle($poster, 'backgroundImage', `url(${option.poster})`)
}
if (option.autoplay) {
$video.autoplay = option.autoplay;
}
if (option.autoplay) {
$video.autoplay = option.autoplay
}
if (option.playsInline) {
$video.playsInline = true;
$video['webkit-playsinline'] = true;
}
if (option.playsInline) {
$video.playsInline = true
$video['webkit-playsinline'] = true
}
if (option.theme) {
option.cssVar['--art-theme'] = option.theme;
}
if (option.theme) {
option.cssVar['--art-theme'] = option.theme
}
for (const key in option.cssVar) {
art.cssVar(key, option.cssVar[key]);
}
for (const key in option.cssVar) {
art.cssVar(key, option.cssVar[key])
}
art.url = option.url;
art.url = option.url
}
+14 -14
View File
@@ -1,18 +1,18 @@
import { def } from '../utils';
import { def } from '../utils'
export default function pauseMix(art) {
const {
template: { $video },
i18n,
notice,
} = art;
const {
template: { $video },
i18n,
notice,
} = art
def(art, 'pause', {
value() {
const result = $video.pause();
notice.show = i18n.get('Pause');
art.emit('pause');
return result;
},
});
def(art, 'pause', {
value() {
const result = $video.pause()
notice.show = i18n.get('Pause')
art.emit('pause')
return result
},
})
}
+72 -68
View File
@@ -1,83 +1,87 @@
import { def } from '../utils';
import { def } from '../utils'
function nativePip(art) {
const {
template: { $video },
proxy,
notice,
} = art;
const {
template: { $video },
proxy,
notice,
} = art
$video.disablePictureInPicture = false;
$video.disablePictureInPicture = false
def(art, 'pip', {
get() {
return document.pictureInPictureElement;
},
set(value) {
if (value) {
art.state = 'pip';
$video.requestPictureInPicture().catch((err) => {
notice.show = err;
throw err;
});
} else {
document.exitPictureInPicture().catch((err) => {
notice.show = err;
throw err;
});
}
},
});
def(art, 'pip', {
get() {
return document.pictureInPictureElement
},
set(value) {
if (value) {
art.state = 'pip'
$video.requestPictureInPicture().catch((err) => {
notice.show = err
throw err
})
}
else {
document.exitPictureInPicture().catch((err) => {
notice.show = err
throw err
})
}
},
})
proxy($video, 'enterpictureinpicture', () => {
art.emit('pip', true);
});
proxy($video, 'enterpictureinpicture', () => {
art.emit('pip', true)
})
proxy($video, 'leavepictureinpicture', () => {
art.emit('pip', false);
});
proxy($video, 'leavepictureinpicture', () => {
art.emit('pip', false)
})
}
function webkitPip(art) {
const { $video } = art.template;
const { $video } = art.template
$video.webkitSetPresentationMode('inline');
$video.webkitSetPresentationMode('inline')
def(art, 'pip', {
get() {
return $video.webkitPresentationMode === 'picture-in-picture';
},
set(value) {
if (value) {
art.state = 'pip';
$video.webkitSetPresentationMode('picture-in-picture');
art.emit('pip', true);
} else {
$video.webkitSetPresentationMode('inline');
art.emit('pip', false);
}
},
});
def(art, 'pip', {
get() {
return $video.webkitPresentationMode === 'picture-in-picture'
},
set(value) {
if (value) {
art.state = 'pip'
$video.webkitSetPresentationMode('picture-in-picture')
art.emit('pip', true)
}
else {
$video.webkitSetPresentationMode('inline')
art.emit('pip', false)
}
},
})
}
export default function pipMix(art) {
const {
i18n,
notice,
template: { $video },
} = art;
if (document.pictureInPictureEnabled) {
nativePip(art);
} else if ($video.webkitSupportsPresentationMode) {
webkitPip(art);
} else {
def(art, 'pip', {
get() {
return false;
},
set() {
notice.show = i18n.get('PIP Not Supported');
},
});
}
const {
i18n,
notice,
template: { $video },
} = art
if (document.pictureInPictureEnabled) {
nativePip(art)
}
else if ($video.webkitSupportsPresentationMode) {
webkitPip(art)
}
else {
def(art, 'pip', {
get() {
return false
},
set() {
notice.show = i18n.get('PIP Not Supported')
},
})
}
}
+24 -24
View File
@@ -1,30 +1,30 @@
import { def } from '../utils';
import { def } from '../utils'
export default function playMix(art) {
const {
i18n,
notice,
option,
constructor: { instances },
template: { $video },
} = art;
const {
i18n,
notice,
option,
constructor: { instances },
template: { $video },
} = art
def(art, 'play', {
value: async function () {
const result = await $video.play();
notice.show = i18n.get('Play');
art.emit('play');
def(art, 'play', {
async value() {
const result = await $video.play()
notice.show = i18n.get('Play')
art.emit('play')
if (option.mutex) {
for (let index = 0; index < instances.length; index++) {
const instance = instances[index];
if (instance !== art) {
instance.pause();
}
}
}
if (option.mutex) {
for (let index = 0; index < instances.length; index++) {
const instance = instances[index]
if (instance !== art) {
instance.pause()
}
}
}
return result;
},
});
return result
},
})
}
@@ -1,24 +1,26 @@
import { def } from '../utils';
import { def } from '../utils'
export default function playbackRateMix(art) {
const {
template: { $video },
i18n,
notice,
} = art;
const {
template: { $video },
i18n,
notice,
} = art
def(art, 'playbackRate', {
get() {
return $video.playbackRate;
},
set(rate) {
if (rate) {
if (rate === $video.playbackRate) return;
$video.playbackRate = rate;
notice.show = `${i18n.get('Rate')}: ${rate === 1.0 ? i18n.get('Normal') : `${rate}x`}`;
} else {
art.playbackRate = 1;
}
},
});
def(art, 'playbackRate', {
get() {
return $video.playbackRate
},
set(rate) {
if (rate) {
if (rate === $video.playbackRate)
return
$video.playbackRate = rate
notice.show = `${i18n.get('Rate')}: ${rate === 1.0 ? i18n.get('Normal') : `${rate}x`}`
}
else {
art.playbackRate = 1
}
},
})
}
+4 -4
View File
@@ -1,7 +1,7 @@
import { def } from '../utils';
import { def } from '../utils'
export default function playedMix(art) {
def(art, 'played', {
get: () => art.currentTime / art.duration,
});
def(art, 'played', {
get: () => art.currentTime / art.duration,
})
}
+9 -8
View File
@@ -1,11 +1,12 @@
import { def } from '../utils';
import { def } from '../utils'
export default function playingMix(art) {
const { $video } = art.template;
def(art, 'playing', {
get: () => {
if (typeof $video.playing === 'boolean') return $video.playing;
return !!($video.currentTime > 0 && !$video.paused && !$video.ended && $video.readyState > 2);
},
});
const { $video } = art.template
def(art, 'playing', {
get: () => {
if (typeof $video.playing === 'boolean')
return $video.playing
return !!($video.currentTime > 0 && !$video.paused && !$video.ended && $video.readyState > 2)
},
})
}
+17 -17
View File
@@ -1,21 +1,21 @@
import { def, setStyle } from '../utils';
import { def, setStyle } from '../utils'
export default function posterMix(art) {
const {
template: { $poster },
} = art;
const {
template: { $poster },
} = art
def(art, 'poster', {
get: () => {
try {
return $poster.style['backgroundImage'].match(/"(.*)"/)[1];
// eslint-disable-next-line no-unused-vars
} catch (error) {
return '';
}
},
set(url) {
setStyle($poster, 'backgroundImage', `url(${url})`);
},
});
def(art, 'poster', {
get: () => {
try {
return $poster.style.backgroundImage.match(/"(.*)"/)[1]
}
catch {
return ''
}
},
set(url) {
setStyle($poster, 'backgroundImage', `url(${url})`)
},
})
}
+21 -21
View File
@@ -1,25 +1,25 @@
import { def } from '../utils';
import { def } from '../utils'
export default function qualityMix(art) {
def(art, 'quality', {
set(quality) {
const { controls, notice, i18n } = art;
const qualityDefault = quality.find((item) => item.default) || quality[0];
controls.update({
name: 'quality',
position: 'right',
index: 10,
style: {
marginRight: '10px',
},
html: qualityDefault?.html || '',
selector: quality,
async onSelect(item) {
await art.switchQuality(item.url);
notice.show = `${i18n.get('Switch Video')}: ${item.html}`;
return item.html;
},
});
def(art, 'quality', {
set(quality) {
const { controls, notice, i18n } = art
const qualityDefault = quality.find(item => item.default) || quality[0]
controls.update({
name: 'quality',
position: 'right',
index: 10,
style: {
marginRight: '10px',
},
});
html: qualityDefault?.html || '',
selector: quality,
async onSelect(item) {
await art.switchQuality(item.url)
notice.show = `${i18n.get('Switch Video')}: ${item.html}`
return item.html
},
})
},
})
}
+25 -25
View File
@@ -1,31 +1,31 @@
import { def, getRect } from '../utils';
import { def, getRect } from '../utils'
export default function rectMix(art) {
def(art, 'rect', {
get: () => {
return getRect(art.template.$player);
},
});
def(art, 'rect', {
get: () => {
return getRect(art.template.$player)
},
})
const keys = ['bottom', 'height', 'left', 'right', 'top', 'width'];
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
def(art, key, {
get: () => {
return art.rect[key];
},
});
}
const keys = ['bottom', 'height', 'left', 'right', 'top', 'width']
for (let index = 0; index < keys.length; index++) {
const key = keys[index]
def(art, key, {
get: () => {
return art.rect[key]
},
})
}
def(art, 'x', {
get: () => {
return art.left + window.pageXOffset;
},
});
def(art, 'x', {
get: () => {
return art.left + window.pageXOffset
},
})
def(art, 'y', {
get: () => {
return art.top + window.pageYOffset;
},
});
def(art, 'y', {
get: () => {
return art.top + window.pageYOffset
},
})
}
+47 -45
View File
@@ -1,52 +1,54 @@
import { secondToTime, download, def, createElement } from '../utils';
import { createElement, def, download, secondToTime } from '../utils'
export default function screenshotMix(art) {
const {
notice,
template: { $video },
} = art;
const {
notice,
template: { $video },
} = art
const $canvas = createElement('canvas');
const $canvas = createElement('canvas')
def(art, 'getDataURL', {
value: () =>
new Promise((resolve, reject) => {
try {
$canvas.width = $video.videoWidth;
$canvas.height = $video.videoHeight;
$canvas.getContext('2d').drawImage($video, 0, 0);
resolve($canvas.toDataURL('image/png'));
} catch (err) {
notice.show = err;
reject(err);
}
}),
});
def(art, 'getDataURL', {
value: () =>
new Promise((resolve, reject) => {
try {
$canvas.width = $video.videoWidth
$canvas.height = $video.videoHeight
$canvas.getContext('2d').drawImage($video, 0, 0)
resolve($canvas.toDataURL('image/png'))
}
catch (err) {
notice.show = err
reject(err)
}
}),
})
def(art, 'getBlobUrl', {
value: () =>
new Promise((resolve, reject) => {
try {
$canvas.width = $video.videoWidth;
$canvas.height = $video.videoHeight;
$canvas.getContext('2d').drawImage($video, 0, 0);
$canvas.toBlob((blob) => {
resolve(URL.createObjectURL(blob));
});
} catch (err) {
notice.show = err;
reject(err);
}
}),
});
def(art, 'getBlobUrl', {
value: () =>
new Promise((resolve, reject) => {
try {
$canvas.width = $video.videoWidth
$canvas.height = $video.videoHeight
$canvas.getContext('2d').drawImage($video, 0, 0)
$canvas.toBlob((blob) => {
resolve(URL.createObjectURL(blob))
})
}
catch (err) {
notice.show = err
reject(err)
}
}),
})
def(art, 'screenshot', {
value: async (name) => {
const dataUri = await art.getDataURL();
const fileName = name || `artplayer_${secondToTime($video.currentTime)}`;
download(dataUri, `${fileName}.png`);
art.emit('screenshot', dataUri);
return dataUri;
},
});
def(art, 'screenshot', {
value: async (name) => {
const dataUri = await art.getDataURL()
const fileName = name || `artplayer_${secondToTime($video.currentTime)}`
download(dataUri, `${fileName}.png`)
art.emit('screenshot', dataUri)
return dataUri
},
})
}
+21 -21
View File
@@ -1,27 +1,27 @@
import { secondToTime, def } from '../utils';
import { def, secondToTime } from '../utils'
export default function seekMix(art) {
const { notice } = art;
const { notice } = art
def(art, 'seek', {
set(time) {
art.currentTime = time;
if (art.duration) {
notice.show = `${secondToTime(art.currentTime)} / ${secondToTime(art.duration)}`;
}
art.emit('seek', art.currentTime);
},
});
def(art, 'seek', {
set(time) {
art.currentTime = time
if (art.duration) {
notice.show = `${secondToTime(art.currentTime)} / ${secondToTime(art.duration)}`
}
art.emit('seek', art.currentTime)
},
})
def(art, 'forward', {
set(time) {
art.seek = art.currentTime + time;
},
});
def(art, 'forward', {
set(time) {
art.seek = art.currentTime + time
},
})
def(art, 'backward', {
set(time) {
art.seek = art.currentTime - time;
},
});
def(art, 'backward', {
set(time) {
art.seek = art.currentTime - time
},
})
}
+13 -13
View File
@@ -1,16 +1,16 @@
import { def } from '../utils';
import { def } from '../utils'
export default function stateMix(art) {
const states = ['mini', 'pip', 'fullscreen', 'fullscreenWeb'];
def(art, 'state', {
get: () => states.find((name) => art[name]) || 'standard',
set(name) {
for (let index = 0; index < states.length; index++) {
const prop = states[index];
if (prop !== name && art[prop]) {
art[prop] = false;
}
}
},
});
const states = ['mini', 'pip', 'fullscreen', 'fullscreenWeb']
def(art, 'state', {
get: () => states.find(name => art[name]) || 'standard',
set(name) {
for (let index = 0; index < states.length; index++) {
const prop = states[index]
if (prop !== name && art[prop]) {
art[prop] = false
}
}
},
})
}
@@ -1,27 +1,28 @@
import { def, clamp } from '../utils';
import { clamp, def } from '../utils'
export default function subtitleOffsetMix(art) {
const { notice, i18n, template } = art;
const { notice, i18n, template } = art
def(art, 'subtitleOffset', {
get() {
return template.$track?.offset || 0;
},
set(value) {
const { cues } = art.subtitle;
if (!template.$track || cues.length === 0) return;
const offset = clamp(value, -10, 10);
template.$track.offset = offset;
for (let index = 0; index < cues.length; index++) {
const cue = cues[index];
cue.originalStartTime = cue.originalStartTime ?? cue.startTime;
cue.originalEndTime = cue.originalEndTime ?? cue.endTime;
cue.startTime = clamp(cue.originalStartTime + offset, 0, art.duration);
cue.endTime = clamp(cue.originalEndTime + offset, 0, art.duration);
}
art.subtitle.update();
notice.show = `${i18n.get('Subtitle Offset')}: ${value}s`;
art.emit('subtitleOffset', value);
},
});
def(art, 'subtitleOffset', {
get() {
return template.$track?.offset || 0
},
set(value) {
const { cues } = art.subtitle
if (!template.$track || cues.length === 0)
return
const offset = clamp(value, -10, 10)
template.$track.offset = offset
for (let index = 0; index < cues.length; index++) {
const cue = cues[index]
cue.originalStartTime = cue.originalStartTime ?? cue.startTime
cue.originalEndTime = cue.originalEndTime ?? cue.endTime
cue.startTime = clamp(cue.originalStartTime + offset, 0, art.duration)
cue.endTime = clamp(cue.originalEndTime + offset, 0, art.duration)
}
art.subtitle.update()
notice.show = `${i18n.get('Subtitle Offset')}: ${value}s`
art.emit('subtitleOffset', value)
},
})
}
+37 -36
View File
@@ -1,49 +1,50 @@
import { def } from '../utils';
import { def } from '../utils'
export default function switchMix(art) {
function switchUrl(url, currentTime) {
return new Promise((resolve, reject) => {
if (url === art.url) return;
const { playing, aspectRatio, playbackRate } = art;
function switchUrl(url, currentTime) {
return new Promise((resolve, reject) => {
if (url === art.url)
return
const { playing, aspectRatio, playbackRate } = art
art.pause();
art.url = url;
art.notice.show = '';
art.pause()
art.url = url
art.notice.show = ''
art.once('video:error', reject);
art.once('video:error', reject)
art.once('video:loadedmetadata', () => {
art.currentTime = currentTime;
});
art.once('video:loadedmetadata', () => {
art.currentTime = currentTime
})
art.once('video:canplay', async () => {
art.playbackRate = playbackRate;
art.aspectRatio = aspectRatio;
art.once('video:canplay', async () => {
art.playbackRate = playbackRate
art.aspectRatio = aspectRatio
if (playing) {
await art.play();
}
if (playing) {
await art.play()
}
art.notice.show = '';
art.notice.show = ''
resolve();
});
});
}
resolve()
})
})
}
def(art, 'switchQuality', {
value: (url) => {
return switchUrl(url, art.currentTime);
},
});
def(art, 'switchQuality', {
value: (url) => {
return switchUrl(url, art.currentTime)
},
})
def(art, 'switchUrl', {
value: (url) => {
return switchUrl(url, 0);
},
});
def(art, 'switchUrl', {
value: (url) => {
return switchUrl(url, 0)
},
})
def(art, 'switch', {
set: art.switchUrl,
});
def(art, 'switch', {
set: art.switchUrl,
})
}
+9 -9
View File
@@ -1,12 +1,12 @@
import { def } from '../utils';
import { def } from '../utils'
export default function themeMix(art) {
def(art, 'theme', {
get() {
return art.cssVar('--art-theme');
},
set(theme) {
art.cssVar('--art-theme', theme);
},
});
def(art, 'theme', {
get() {
return art.cssVar('--art-theme')
},
set(theme) {
art.cssVar('--art-theme', theme)
},
})
}
+95 -89
View File
@@ -1,105 +1,111 @@
import { def, setStyle, isMobile, loadImg } from '../utils';
import { def, isMobile, loadImg, setStyle } from '../utils'
export default function thumbnailsMix(art) {
const {
events,
option,
template: { $progress, $video },
} = art;
const {
events,
option,
template: { $progress, $video },
} = art
let timer = null;
let image = null;
let loading = false;
let isLoad = false;
let isHover = false;
let timer = null
let image = null
let loading = false
let isLoad = false
let isHover = false
function reset() {
clearTimeout(timer);
timer = null;
image = null;
loading = false;
isLoad = false;
function reset() {
clearTimeout(timer)
timer = null
image = null
loading = false
isLoad = false
}
function showThumbnails(posWidth) {
const $thumbnails = art.controls?.thumbnails
if (!$thumbnails)
return
const { number, column, width, height, scale } = option.thumbnails
const width2 = width * scale || image.naturalWidth / column
const height2 = height * scale || width2 / ($video.videoWidth / $video.videoHeight)
const perWidth = $progress.clientWidth / number
const perIndex = Math.floor(posWidth / perWidth)
const yIndex = Math.ceil(perIndex / column) - 1
const xIndex = perIndex % column || column - 1
setStyle($thumbnails, 'backgroundImage', `url(${image.src})`)
setStyle($thumbnails, 'height', `${height2}px`)
setStyle($thumbnails, 'width', `${width2}px`)
setStyle($thumbnails, 'backgroundPosition', `-${xIndex * width2}px -${yIndex * height2}px`)
if (posWidth <= width2 / 2) {
setStyle($thumbnails, 'left', 0)
}
function showThumbnails(posWidth) {
const $thumbnails = art.controls?.thumbnails;
if (!$thumbnails) return;
const { number, column, width, height, scale } = option.thumbnails;
const width2 = width * scale || image.naturalWidth / column;
const height2 = height * scale || width2 / ($video.videoWidth / $video.videoHeight);
const perWidth = $progress.clientWidth / number;
const perIndex = Math.floor(posWidth / perWidth);
const yIndex = Math.ceil(perIndex / column) - 1;
const xIndex = perIndex % column || column - 1;
setStyle($thumbnails, 'backgroundImage', `url(${image.src})`);
setStyle($thumbnails, 'height', `${height2}px`);
setStyle($thumbnails, 'width', `${width2}px`);
setStyle($thumbnails, 'backgroundPosition', `-${xIndex * width2}px -${yIndex * height2}px`);
if (posWidth <= width2 / 2) {
setStyle($thumbnails, 'left', 0);
} else if (posWidth > $progress.clientWidth - width2 / 2) {
setStyle($thumbnails, 'left', `${$progress.clientWidth - width2}px`);
} else {
setStyle($thumbnails, 'left', `${posWidth - width2 / 2}px`);
}
else if (posWidth > $progress.clientWidth - width2 / 2) {
setStyle($thumbnails, 'left', `${$progress.clientWidth - width2}px`)
}
else {
setStyle($thumbnails, 'left', `${posWidth - width2 / 2}px`)
}
}
events.hover(
$progress,
() => {
isHover = true;
},
() => {
isHover = false;
},
);
events.hover(
$progress,
() => {
isHover = true
},
() => {
isHover = false
},
)
art.on('setBar', async (type, percentage, event) => {
const $thumbnails = art.controls?.thumbnails;
const { url, scale } = option.thumbnails;
if (!$thumbnails || !url) return;
art.on('setBar', async (type, percentage, event) => {
const $thumbnails = art.controls?.thumbnails
const { url, scale } = option.thumbnails
if (!$thumbnails || !url)
return
const isMobileDragging = type === 'played' && event && isMobile;
const isMobileDragging = type === 'played' && event && isMobile
if (type === 'hover' || isMobileDragging) {
if (!loading) {
loading = true;
image = await loadImg(url, scale);
isLoad = true;
}
if (type === 'hover' || isMobileDragging) {
if (!loading) {
loading = true
image = await loadImg(url, scale)
isLoad = true
}
if (!isLoad || !isHover) return;
if (!isLoad || !isHover)
return
const width = $progress.clientWidth * percentage;
setStyle($thumbnails, 'display', 'flex');
const width = $progress.clientWidth * percentage
setStyle($thumbnails, 'display', 'flex')
if (width > 0 && width < $progress.clientWidth) {
showThumbnails(width);
} else {
if (!isMobile) {
setStyle($thumbnails, 'display', 'none');
}
}
if (isMobileDragging) {
clearTimeout(timer);
timer = setTimeout(() => {
setStyle($thumbnails, 'display', 'none');
}, 500);
}
if (width > 0 && width < $progress.clientWidth) {
showThumbnails(width)
}
else {
if (!isMobile) {
setStyle($thumbnails, 'display', 'none')
}
});
}
def(art, 'thumbnails', {
get() {
return art.option.thumbnails;
},
set(thumbnails) {
if (thumbnails.url && !art.option.isLive) {
art.option.thumbnails = thumbnails;
reset();
}
},
});
if (isMobileDragging) {
clearTimeout(timer)
timer = setTimeout(() => {
setStyle($thumbnails, 'display', 'none')
}, 500)
}
}
})
def(art, 'thumbnails', {
get() {
return art.option.thumbnails
},
set(thumbnails) {
if (thumbnails.url && !art.option.isLive) {
art.option.thumbnails = thumbnails
reset()
}
},
})
}
+11 -10
View File
@@ -1,13 +1,14 @@
import { def } from '../utils';
import { def } from '../utils'
export default function toggleMix(art) {
def(art, 'toggle', {
value() {
if (art.playing) {
return art.pause();
} else {
return art.play();
}
},
});
def(art, 'toggle', {
value() {
if (art.playing) {
return art.pause()
}
else {
return art.play()
}
},
})
}
+9 -9
View File
@@ -1,12 +1,12 @@
import { def } from '../utils';
import { def } from '../utils'
export default function typeMix(art) {
def(art, 'type', {
get() {
return art.option.type;
},
set(type) {
art.option.type = type;
},
});
def(art, 'type', {
get() {
return art.option.type
},
set(type) {
art.option.type = type
},
})
}
+38 -36
View File
@@ -1,42 +1,44 @@
import { getExt, def, sleep } from '../utils';
import { def, getExt, sleep } from '../utils'
export default function urlMix(art) {
const {
option,
template: { $video },
} = art;
const {
option,
template: { $video },
} = art
def(art, 'url', {
get() {
return $video.src;
},
async set(newUrl) {
if (newUrl) {
const oldUrl = art.url;
const typeName = option.type || getExt(newUrl);
const typeCallback = option.customType[typeName];
def(art, 'url', {
get() {
return $video.src
},
async set(newUrl) {
if (newUrl) {
const oldUrl = art.url
const typeName = option.type || getExt(newUrl)
const typeCallback = option.customType[typeName]
if (typeName && typeCallback) {
await sleep();
art.loading.show = true;
typeCallback.call(art, $video, newUrl, art);
} else {
URL.revokeObjectURL(oldUrl);
$video.src = newUrl;
}
if (typeName && typeCallback) {
await sleep()
art.loading.show = true
typeCallback.call(art, $video, newUrl, art)
}
else {
URL.revokeObjectURL(oldUrl)
$video.src = newUrl
}
if (oldUrl !== art.url) {
art.option.url = newUrl;
if (art.isReady && oldUrl) {
art.once('video:canplay', () => {
art.emit('restart', newUrl);
});
}
}
} else {
await sleep();
art.loading.show = true;
}
},
});
if (oldUrl !== art.url) {
art.option.url = newUrl
if (art.isReady && oldUrl) {
art.once('video:canplay', () => {
art.emit('restart', newUrl)
})
}
}
}
else {
await sleep()
art.loading.show = true
}
},
})
}
+24 -24
View File
@@ -1,29 +1,29 @@
import { clamp, def } from '../utils';
import { clamp, def } from '../utils'
export default function volumeMix(art) {
const {
template: { $video },
i18n,
notice,
storage,
} = art;
const {
template: { $video },
i18n,
notice,
storage,
} = art
def(art, 'volume', {
get: () => $video.volume || 0,
set: (percentage) => {
$video.volume = clamp(percentage, 0, 1);
notice.show = `${i18n.get('Volume')}: ${parseInt($video.volume * 100, 10)}`;
if ($video.volume !== 0) {
storage.set('volume', $video.volume);
}
},
});
def(art, 'volume', {
get: () => $video.volume || 0,
set: (percentage) => {
$video.volume = clamp(percentage, 0, 1)
notice.show = `${i18n.get('Volume')}: ${Number.parseInt($video.volume * 100, 10)}`
if ($video.volume !== 0) {
storage.set('volume', $video.volume)
}
},
})
def(art, 'muted', {
get: () => $video.muted,
set: (muted) => {
$video.muted = muted;
art.emit('muted', muted);
},
});
def(art, 'muted', {
get: () => $video.muted,
set: (muted) => {
$video.muted = muted
art.emit('muted', muted)
},
})
}
@@ -1,66 +1,69 @@
import { setStyle, addClass, removeClass, hasClass } from '../utils';
import { addClass, hasClass, removeClass, setStyle } from '../utils'
export default function autoOrientation(art) {
const {
constructor,
template: { $player, $video },
} = art;
const {
constructor,
template: { $player, $video },
} = art
art.on('fullscreenWeb', (state) => {
if (state) {
const { videoWidth, videoHeight } = $video;
const { clientWidth: viewWidth, clientHeight: viewHeight } = document.documentElement;
if (
(videoWidth > videoHeight && viewWidth < viewHeight) ||
(videoWidth < videoHeight && viewWidth > viewHeight)
) {
// There is a conflict with the fullscreen event, and it is changed to asynchronous execution
setTimeout(() => {
setStyle($player, 'width', `${viewHeight}px`);
setStyle($player, 'height', `${viewWidth}px`);
setStyle($player, 'transform-origin', '0 0');
setStyle($player, 'transform', `rotate(90deg) translate(0, -${viewWidth}px)`);
addClass($player, 'art-auto-orientation');
art.isRotate = true;
art.emit('resize');
}, constructor.AUTO_ORIENTATION_TIME);
}
} else {
if (hasClass($player, 'art-auto-orientation')) {
removeClass($player, 'art-auto-orientation');
art.isRotate = false;
art.emit('resize');
}
}
});
art.on('fullscreenWeb', (state) => {
if (state) {
const { videoWidth, videoHeight } = $video
const { clientWidth: viewWidth, clientHeight: viewHeight } = document.documentElement
if (
(videoWidth > videoHeight && viewWidth < viewHeight)
|| (videoWidth < videoHeight && viewWidth > viewHeight)
) {
// There is a conflict with the fullscreen event, and it is changed to asynchronous execution
setTimeout(() => {
setStyle($player, 'width', `${viewHeight}px`)
setStyle($player, 'height', `${viewWidth}px`)
setStyle($player, 'transform-origin', '0 0')
setStyle($player, 'transform', `rotate(90deg) translate(0, -${viewWidth}px)`)
addClass($player, 'art-auto-orientation')
art.isRotate = true
art.emit('resize')
}, constructor.AUTO_ORIENTATION_TIME)
}
}
else {
if (hasClass($player, 'art-auto-orientation')) {
removeClass($player, 'art-auto-orientation')
art.isRotate = false
art.emit('resize')
}
}
})
art.on('fullscreen', async (state) => {
if (!screen?.orientation?.lock) return;
art.on('fullscreen', async (state) => {
if (!screen?.orientation?.lock)
return
const lastOrientation = screen.orientation.type;
if (state) {
const { videoWidth, videoHeight } = $video;
const { clientWidth: viewWidth, clientHeight: viewHeight } = document.documentElement;
if (
(videoWidth > videoHeight && viewWidth < viewHeight) ||
(videoWidth < videoHeight && viewWidth > viewHeight)
) {
const oppositeOrientation = lastOrientation.startsWith('portrait') ? 'landscape' : 'portrait';
await screen.orientation.lock(oppositeOrientation);
addClass($player, 'art-auto-orientation-fullscreen');
}
} else {
if (hasClass($player, 'art-auto-orientation-fullscreen')) {
await screen.orientation.lock(lastOrientation);
removeClass($player, 'art-auto-orientation-fullscreen');
}
}
});
const lastOrientation = screen.orientation.type
if (state) {
const { videoWidth, videoHeight } = $video
const { clientWidth: viewWidth, clientHeight: viewHeight } = document.documentElement
if (
(videoWidth > videoHeight && viewWidth < viewHeight)
|| (videoWidth < videoHeight && viewWidth > viewHeight)
) {
const oppositeOrientation = lastOrientation.startsWith('portrait') ? 'landscape' : 'portrait'
await screen.orientation.lock(oppositeOrientation)
addClass($player, 'art-auto-orientation-fullscreen')
}
}
else {
if (hasClass($player, 'art-auto-orientation-fullscreen')) {
await screen.orientation.lock(lastOrientation)
removeClass($player, 'art-auto-orientation-fullscreen')
}
}
})
return {
name: 'autoOrientation',
get state() {
return hasClass($player, 'art-auto-orientation');
},
};
return {
name: 'autoOrientation',
get state() {
return hasClass($player, 'art-auto-orientation')
},
}
}
+77 -77
View File
@@ -1,91 +1,91 @@
import { secondToTime, setStyle, query, append } from '../utils';
import { append, query, secondToTime, setStyle } from '../utils'
export default function autoPlayback(art) {
const {
i18n,
icons,
storage,
constructor,
proxy,
template: { $poster },
} = art;
const {
i18n,
icons,
storage,
constructor,
proxy,
template: { $poster },
} = art
const $autoPlayback = art.layers.add({
name: 'auto-playback',
html: `
const $autoPlayback = art.layers.add({
name: 'auto-playback',
html: `
<div class="art-auto-playback-close"></div>
<div class="art-auto-playback-last"></div>
<div class="art-auto-playback-jump"></div>
`,
});
})
const $last = query('.art-auto-playback-last', $autoPlayback);
const $jump = query('.art-auto-playback-jump', $autoPlayback);
const $close = query('.art-auto-playback-close', $autoPlayback);
append($close, icons.close);
const $last = query('.art-auto-playback-last', $autoPlayback)
const $jump = query('.art-auto-playback-jump', $autoPlayback)
const $close = query('.art-auto-playback-close', $autoPlayback)
append($close, icons.close)
let timer = null;
let timer = null
art.on('video:timeupdate', () => {
if (art.playing) {
const times = storage.get('times') || {};
const keys = Object.keys(times);
if (keys.length > constructor.AUTO_PLAYBACK_MAX) {
delete times[keys[0]];
}
times[art.option.id || art.option.url] = art.currentTime;
storage.set('times', times);
}
});
function init() {
const times = storage.get('times') || {};
const currentTime = times[art.option.id || art.option.url];
clearTimeout(timer);
setStyle($autoPlayback, 'display', 'none');
if (currentTime && currentTime >= constructor.AUTO_PLAYBACK_MIN) {
setStyle($autoPlayback, 'display', 'flex');
$last.innerText = `${i18n.get('Last Seen')} ${secondToTime(currentTime)}`;
$jump.innerText = i18n.get('Jump Play');
proxy($close, 'click', () => {
setStyle($autoPlayback, 'display', 'none');
});
proxy($jump, 'click', () => {
art.seek = currentTime;
art.play();
setStyle($poster, 'display', 'none');
setStyle($autoPlayback, 'display', 'none');
});
art.once('video:timeupdate', () => {
timer = setTimeout(() => {
setStyle($autoPlayback, 'display', 'none');
}, constructor.AUTO_PLAYBACK_TIMEOUT);
});
}
art.on('video:timeupdate', () => {
if (art.playing) {
const times = storage.get('times') || {}
const keys = Object.keys(times)
if (keys.length > constructor.AUTO_PLAYBACK_MAX) {
delete times[keys[0]]
}
times[art.option.id || art.option.url] = art.currentTime
storage.set('times', times)
}
})
art.on('ready', init);
art.on('restart', init);
function init() {
const times = storage.get('times') || {}
const currentTime = times[art.option.id || art.option.url]
return {
name: 'auto-playback',
get times() {
return storage.get('times') || {};
},
clear() {
return storage.del('times');
},
delete(id) {
const times = storage.get('times') || {};
delete times[id];
storage.set('times', times);
return times;
},
};
clearTimeout(timer)
setStyle($autoPlayback, 'display', 'none')
if (currentTime && currentTime >= constructor.AUTO_PLAYBACK_MIN) {
setStyle($autoPlayback, 'display', 'flex')
$last.textContent = `${i18n.get('Last Seen')} ${secondToTime(currentTime)}`
$jump.textContent = i18n.get('Jump Play')
proxy($close, 'click', () => {
setStyle($autoPlayback, 'display', 'none')
})
proxy($jump, 'click', () => {
art.seek = currentTime
art.play()
setStyle($poster, 'display', 'none')
setStyle($autoPlayback, 'display', 'none')
})
art.once('video:timeupdate', () => {
timer = setTimeout(() => {
setStyle($autoPlayback, 'display', 'none')
}, constructor.AUTO_PLAYBACK_TIMEOUT)
})
}
}
art.on('ready', init)
art.on('restart', init)
return {
name: 'auto-playback',
get times() {
return storage.get('times') || {}
},
clear() {
return storage.del('times')
},
delete(id) {
const times = storage.get('times') || {}
delete times[id]
storage.set('times', times)
return times
},
}
}
+36 -36
View File
@@ -1,44 +1,44 @@
import { hasClass, addClass, removeClass } from '../utils';
import { addClass, hasClass, removeClass } from '../utils'
export default function fastForward(art) {
const {
constructor,
proxy,
template: { $player, $video },
} = art;
const {
constructor,
proxy,
template: { $player, $video },
} = art
let timer = null;
let isPress = false;
let lastPlaybackRate = 1;
let timer = null
let isPress = false
let lastPlaybackRate = 1
const onStart = (event) => {
if (event.touches.length === 1 && art.playing && !art.isLock) {
timer = setTimeout(() => {
isPress = true;
lastPlaybackRate = art.playbackRate;
art.playbackRate = constructor.FAST_FORWARD_VALUE;
addClass($player, 'art-fast-forward');
}, constructor.FAST_FORWARD_TIME);
}
};
const onStart = (event) => {
if (event.touches.length === 1 && art.playing && !art.isLock) {
timer = setTimeout(() => {
isPress = true
lastPlaybackRate = art.playbackRate
art.playbackRate = constructor.FAST_FORWARD_VALUE
addClass($player, 'art-fast-forward')
}, constructor.FAST_FORWARD_TIME)
}
}
const onStop = () => {
clearTimeout(timer);
if (isPress) {
isPress = false;
art.playbackRate = lastPlaybackRate;
removeClass($player, 'art-fast-forward');
}
};
const onStop = () => {
clearTimeout(timer)
if (isPress) {
isPress = false
art.playbackRate = lastPlaybackRate
removeClass($player, 'art-fast-forward')
}
}
proxy($video, 'touchstart', onStart);
proxy(document, 'touchmove', onStop);
proxy(document, 'touchend', onStop);
proxy($video, 'touchstart', onStart)
proxy(document, 'touchmove', onStop)
proxy(document, 'touchend', onStop)
return {
name: 'fastForward',
get state() {
return hasClass($player, 'art-fast-forward');
},
};
return {
name: 'fastForward',
get state() {
return hasClass($player, 'art-fast-forward')
},
}
}
+49 -48
View File
@@ -1,58 +1,59 @@
import { errorHandle, isMobile, has, def } from '../utils';
import miniProgressBar from './miniProgressBar';
import autoOrientation from './autoOrientation';
import autoPlayback from './autoPlayback';
import fastForward from './fastForward';
import lock from './lock';
import { def, errorHandle, has, isMobile } from '../utils'
import autoOrientation from './autoOrientation'
import autoPlayback from './autoPlayback'
import fastForward from './fastForward'
import lock from './lock'
import miniProgressBar from './miniProgressBar'
export default class Plugins {
constructor(art) {
this.art = art;
this.id = 0;
constructor(art) {
this.art = art
this.id = 0
const { option } = art;
const { option } = art
if (option.miniProgressBar && !option.isLive) {
this.add(miniProgressBar);
}
if (option.lock && isMobile) {
this.add(lock);
}
if (option.autoPlayback && !option.isLive) {
this.add(autoPlayback);
}
if (option.autoOrientation && isMobile) {
this.add(autoOrientation);
}
if (option.fastForward && isMobile && !option.isLive) {
this.add(fastForward);
}
for (let index = 0; index < option.plugins.length; index++) {
this.add(option.plugins[index]);
}
if (option.miniProgressBar && !option.isLive) {
this.add(miniProgressBar)
}
add(plugin) {
this.id += 1;
const result = plugin.call(this.art, this.art);
if (result instanceof Promise) {
return result.then((res) => this.next(plugin, res));
} else {
return this.next(plugin, result);
}
if (option.lock && isMobile) {
this.add(lock)
}
next(plugin, result) {
const pluginName = (result && result.name) || plugin.name || `plugin${this.id}`;
errorHandle(!has(this, pluginName), `Cannot add a plugin that already has the same name: ${pluginName}`);
def(this, pluginName, {
value: result,
});
return this;
if (option.autoPlayback && !option.isLive) {
this.add(autoPlayback)
}
if (option.autoOrientation && isMobile) {
this.add(autoOrientation)
}
if (option.fastForward && isMobile && !option.isLive) {
this.add(fastForward)
}
for (let index = 0; index < option.plugins.length; index++) {
this.add(option.plugins[index])
}
}
add(plugin) {
this.id += 1
const result = plugin.call(this.art, this.art)
if (result instanceof Promise) {
return result.then(res => this.next(plugin, res))
}
else {
return this.next(plugin, result)
}
}
next(plugin, result) {
const pluginName = (result && result.name) || plugin.name || `plugin${this.id}`
errorHandle(!has(this, pluginName), `Cannot add a plugin that already has the same name: ${pluginName}`)
def(this, pluginName, {
value: result,
})
return this
}
}
+59 -56
View File
@@ -1,65 +1,68 @@
import { append, setStyle, hasClass, addClass, removeClass } from '../utils';
import { addClass, append, hasClass, removeClass, setStyle } from '../utils'
export default function lock(art) {
const {
layers,
icons,
template: { $player },
} = art;
const {
layers,
icons,
template: { $player },
} = art
function getState() {
return hasClass($player, 'art-lock');
}
function getState() {
return hasClass($player, 'art-lock')
}
function setLock() {
addClass($player, 'art-lock');
art.isLock = true;
art.emit('lock', true);
}
function setLock() {
addClass($player, 'art-lock')
art.isLock = true
art.emit('lock', true)
}
function setUnlock() {
removeClass($player, 'art-lock');
art.isLock = false;
art.emit('lock', false);
}
function setUnlock() {
removeClass($player, 'art-lock')
art.isLock = false
art.emit('lock', false)
}
layers.add({
name: 'lock',
mounted($el) {
const $lock = append($el, icons.lock);
const $unlock = append($el, icons.unlock);
setStyle($lock, 'display', 'none');
layers.add({
name: 'lock',
mounted($el) {
const $lock = append($el, icons.lock)
const $unlock = append($el, icons.unlock)
setStyle($lock, 'display', 'none')
art.on('lock', (state) => {
if (state) {
setStyle($lock, 'display', 'inline-flex');
setStyle($unlock, 'display', 'none');
} else {
setStyle($lock, 'display', 'none');
setStyle($unlock, 'display', 'inline-flex');
}
});
},
click() {
if (getState()) {
setUnlock();
} else {
setLock();
}
},
});
art.on('lock', (state) => {
if (state) {
setStyle($lock, 'display', 'inline-flex')
setStyle($unlock, 'display', 'none')
}
else {
setStyle($lock, 'display', 'none')
setStyle($unlock, 'display', 'inline-flex')
}
})
},
click() {
if (getState()) {
setUnlock()
}
else {
setLock()
}
},
})
return {
name: 'lock',
get state() {
return getState();
},
set state(value) {
if (value) {
setLock();
} else {
setUnlock();
}
},
};
return {
name: 'lock',
get state() {
return getState()
},
set state(value) {
if (value) {
setLock()
}
else {
setUnlock()
}
},
}
}
@@ -1,13 +1,14 @@
import { addClass, removeClass } from '../utils';
import { addClass, removeClass } from '../utils'
export default function miniProgressBar(art) {
art.on('control', (state) => {
if (state) {
removeClass(art.template.$player, 'art-mini-progress-bar');
} else {
addClass(art.template.$player, 'art-mini-progress-bar');
}
});
art.on('control', (state) => {
if (state) {
removeClass(art.template.$player, 'art-mini-progress-bar')
}
else {
addClass(art.template.$player, 'art-mini-progress-bar')
}
})
return { name: 'mini-progress-bar' };
return { name: 'mini-progress-bar' }
}
+114 -114
View File
@@ -1,124 +1,124 @@
import { errorHandle } from '../utils';
import { errorHandle } from '../utils'
const a = 'array';
const b = 'boolean';
const s = 'string';
const n = 'number';
const o = 'object';
const f = 'function';
const a = 'array'
const b = 'boolean'
const s = 'string'
const n = 'number'
const o = 'object'
const f = 'function'
function validElement(value, type, paths) {
return errorHandle(
type === s || type === n || value instanceof Element,
`${paths.join('.')} require '${s}' or 'Element' type`,
);
return errorHandle(
type === s || type === n || value instanceof Element,
`${paths.join('.')} require '${s}' or 'Element' type`,
)
}
export const ComponentOption = {
html: validElement,
disable: `?${b}`,
name: `?${s}`,
index: `?${n}`,
style: `?${o}`,
click: `?${f}`,
mounted: `?${f}`,
tooltip: `?${s}|${n}`,
width: `?${n}`,
selector: `?${a}`,
onSelect: `?${f}`,
switch: `?${b}`,
onSwitch: `?${f}`,
range: `?${a}`,
onRange: `?${f}`,
onChange: `?${f}`,
};
html: validElement,
disable: `?${b}`,
name: `?${s}`,
index: `?${n}`,
style: `?${o}`,
click: `?${f}`,
mounted: `?${f}`,
tooltip: `?${s}|${n}`,
width: `?${n}`,
selector: `?${a}`,
onSelect: `?${f}`,
switch: `?${b}`,
onSwitch: `?${f}`,
range: `?${a}`,
onRange: `?${f}`,
onChange: `?${f}`,
}
export default {
id: s,
container: validElement,
id: s,
container: validElement,
url: s,
poster: s,
type: s,
theme: s,
lang: s,
volume: n,
isLive: b,
muted: b,
autoplay: b,
autoSize: b,
autoMini: b,
loop: b,
flip: b,
playbackRate: b,
aspectRatio: b,
screenshot: b,
setting: b,
hotkey: b,
pip: b,
mutex: b,
backdrop: b,
fullscreen: b,
fullscreenWeb: b,
subtitleOffset: b,
miniProgressBar: b,
useSSR: b,
playsInline: b,
lock: b,
gesture: b,
fastForward: b,
autoPlayback: b,
autoOrientation: b,
airplay: b,
proxy: `?${f}`,
plugins: [f],
layers: [ComponentOption],
contextmenu: [ComponentOption],
settings: [ComponentOption],
controls: [
{
...ComponentOption,
position: (value, _, paths) => {
const position = ['top', 'left', 'right']
return errorHandle(
position.includes(value),
`${paths.join('.')} only accept ${position.toString()} as parameters`,
)
},
},
],
quality: [
{
default: `?${b}`,
html: s,
url: s,
},
],
highlight: [
{
time: n,
text: s,
},
],
thumbnails: {
url: s,
poster: s,
number: n,
column: n,
width: n,
height: n,
scale: n,
},
subtitle: {
url: s,
name: s,
type: s,
theme: s,
lang: s,
volume: n,
isLive: b,
muted: b,
autoplay: b,
autoSize: b,
autoMini: b,
loop: b,
flip: b,
playbackRate: b,
aspectRatio: b,
screenshot: b,
setting: b,
hotkey: b,
pip: b,
mutex: b,
backdrop: b,
fullscreen: b,
fullscreenWeb: b,
subtitleOffset: b,
miniProgressBar: b,
useSSR: b,
playsInline: b,
lock: b,
gesture: b,
fastForward: b,
autoPlayback: b,
autoOrientation: b,
airplay: b,
proxy: `?${f}`,
plugins: [f],
layers: [ComponentOption],
contextmenu: [ComponentOption],
settings: [ComponentOption],
controls: [
{
...ComponentOption,
position: (value, _, paths) => {
const position = ['top', 'left', 'right'];
return errorHandle(
position.includes(value),
`${paths.join('.')} only accept ${position.toString()} as parameters`,
);
},
},
],
quality: [
{
default: `?${b}`,
html: s,
url: s,
},
],
highlight: [
{
time: n,
text: s,
},
],
thumbnails: {
url: s,
number: n,
column: n,
width: n,
height: n,
scale: n,
},
subtitle: {
url: s,
name: s,
type: s,
style: o,
escape: b,
encoding: s,
onVttLoad: f,
},
moreVideoAttr: o,
i18n: o,
icons: o,
cssVar: o,
customType: o,
};
style: o,
escape: b,
encoding: s,
onVttLoad: f,
},
moreVideoAttr: o,
i18n: o,
icons: o,
cssVar: o,
customType: o,
}
+35 -35
View File
@@ -1,40 +1,40 @@
export default function aspectRatio(art) {
const {
i18n,
icons,
constructor: { SETTING_ITEM_WIDTH, ASPECT_RATIO },
} = art;
const {
i18n,
icons,
constructor: { SETTING_ITEM_WIDTH, ASPECT_RATIO },
} = art
function getI18n(value) {
return value === 'default' ? i18n.get('Default') : value;
}
function getI18n(value) {
return value === 'default' ? i18n.get('Default') : value
}
function update() {
const target = art.setting.find(`aspect-ratio-${art.aspectRatio}`);
art.setting.check(target);
}
function update() {
const target = art.setting.find(`aspect-ratio-${art.aspectRatio}`)
art.setting.check(target)
}
return {
width: SETTING_ITEM_WIDTH,
name: 'aspect-ratio',
html: i18n.get('Aspect Ratio'),
icon: icons.aspectRatio,
tooltip: getI18n(art.aspectRatio),
selector: ASPECT_RATIO.map((item) => {
return {
value: item,
name: `aspect-ratio-${item}`,
default: item === art.aspectRatio,
html: getI18n(item),
};
}),
onSelect(item) {
art.aspectRatio = item.value;
return item.html;
},
mounted: () => {
update();
art.on('aspectRatio', () => update());
},
};
return {
width: SETTING_ITEM_WIDTH,
name: 'aspect-ratio',
html: i18n.get('Aspect Ratio'),
icon: icons.aspectRatio,
tooltip: getI18n(art.aspectRatio),
selector: ASPECT_RATIO.map((item) => {
return {
value: item,
name: `aspect-ratio-${item}`,
default: item === art.aspectRatio,
html: getI18n(item),
}
}),
onSelect(item) {
art.aspectRatio = item.value
return item.html
},
mounted: () => {
update()
art.on('aspectRatio', () => update())
},
}
}
+36 -36
View File
@@ -1,42 +1,42 @@
import { capitalize } from '../utils';
import { capitalize } from '../utils'
export default function flip(art) {
const {
i18n,
icons,
constructor: { SETTING_ITEM_WIDTH, FLIP },
} = art;
const {
i18n,
icons,
constructor: { SETTING_ITEM_WIDTH, FLIP },
} = art
function getI18n(value) {
return i18n.get(capitalize(value));
}
function getI18n(value) {
return i18n.get(capitalize(value))
}
function update() {
const target = art.setting.find(`flip-${art.flip}`);
art.setting.check(target);
}
function update() {
const target = art.setting.find(`flip-${art.flip}`)
art.setting.check(target)
}
return {
width: SETTING_ITEM_WIDTH,
name: 'flip',
html: i18n.get('Video Flip'),
tooltip: getI18n(art.flip),
icon: icons.flip,
selector: FLIP.map((item) => {
return {
value: item,
name: `flip-${item}`,
default: item === art.flip,
html: getI18n(item),
};
}),
onSelect(item) {
art.flip = item.value;
return item.html;
},
mounted: () => {
update();
art.on('flip', () => update());
},
};
return {
width: SETTING_ITEM_WIDTH,
name: 'flip',
html: i18n.get('Video Flip'),
tooltip: getI18n(art.flip),
icon: icons.flip,
selector: FLIP.map((item) => {
return {
value: item,
name: `flip-${item}`,
default: item === art.flip,
html: getI18n(item),
}
}),
onSelect(item) {
art.flip = item.value
return item.html
},
mounted: () => {
update()
art.on('flip', () => update())
},
}
}
File diff suppressed because it is too large. Load diff
+35 -35
View File
@@ -1,40 +1,40 @@
export default function playbackRate(art) {
const {
i18n,
icons,
constructor: { SETTING_ITEM_WIDTH, PLAYBACK_RATE },
} = art;
const {
i18n,
icons,
constructor: { SETTING_ITEM_WIDTH, PLAYBACK_RATE },
} = art
function getI18n(value) {
return value === 1.0 ? i18n.get('Normal') : value.toFixed(1);
}
function getI18n(value) {
return value === 1.0 ? i18n.get('Normal') : value.toFixed(1)
}
function update() {
const target = art.setting.find(`playback-rate-${art.playbackRate}`);
art.setting.check(target);
}
function update() {
const target = art.setting.find(`playback-rate-${art.playbackRate}`)
art.setting.check(target)
}
return {
width: SETTING_ITEM_WIDTH,
name: 'playback-rate',
html: i18n.get('Play Speed'),
tooltip: getI18n(art.playbackRate),
icon: icons.playbackRate,
selector: PLAYBACK_RATE.map((item) => {
return {
value: item,
name: `playback-rate-${item}`,
default: item === art.playbackRate,
html: getI18n(item),
};
}),
onSelect(item) {
art.playbackRate = item.value;
return item.html;
},
mounted: () => {
update();
art.on('video:ratechange', () => update());
},
};
return {
width: SETTING_ITEM_WIDTH,
name: 'playback-rate',
html: i18n.get('Play Speed'),
tooltip: getI18n(art.playbackRate),
icon: icons.playbackRate,
selector: PLAYBACK_RATE.map((item) => {
return {
value: item,
name: `playback-rate-${item}`,
default: item === art.playbackRate,
html: getI18n(item),
}
}),
onSelect(item) {
art.playbackRate = item.value
return item.html
},
mounted: () => {
update()
art.on('video:ratechange', () => update())
},
}
}
@@ -1,22 +1,22 @@
export default function subtitleOffset(art) {
const { i18n, icons, constructor } = art;
const { i18n, icons, constructor } = art
return {
width: constructor.SETTING_ITEM_WIDTH,
name: 'subtitle-offset',
html: i18n.get('Subtitle Offset'),
icon: icons.subtitle,
tooltip: '0s',
range: [0, -10, 10, 0.1],
onChange(item) {
art.subtitleOffset = item.range[0];
return item.range[0] + 's';
},
mounted: (_, item) => {
art.on('subtitleOffset', (value) => {
item.$range.value = value;
item.tooltip = value + 's';
});
},
};
return {
width: constructor.SETTING_ITEM_WIDTH,
name: 'subtitle-offset',
html: i18n.get('Subtitle Offset'),
icon: icons.subtitle,
tooltip: '0s',
range: [0, -10, 10, 0.1],
onChange(item) {
art.subtitleOffset = item.range[0]
return `${item.range[0]}s`
},
mounted: (_, item) => {
art.on('subtitleOffset', (value) => {
item.$range.value = value
item.tooltip = `${value}s`
})
},
}
}
+38 -38
View File
@@ -1,48 +1,48 @@
export default class Storage {
constructor() {
this.name = 'artplayer_settings';
this.settings = {};
}
constructor() {
this.name = 'artplayer_settings'
this.settings = {}
}
get(key) {
try {
const storage = JSON.parse(window.localStorage.getItem(this.name)) || {};
return key ? storage[key] : storage;
// eslint-disable-next-line no-unused-vars
} catch (error) {
return key ? this.settings[key] : this.settings;
}
get(key) {
try {
const storage = JSON.parse(window.localStorage.getItem(this.name)) || {}
return key ? storage[key] : storage
}
catch {
return key ? this.settings[key] : this.settings
}
}
set(key, value) {
try {
const storage = Object.assign({}, this.get(), {
[key]: value,
});
window.localStorage.setItem(this.name, JSON.stringify(storage));
// eslint-disable-next-line no-unused-vars
} catch (error) {
this.settings[key] = value;
}
set(key, value) {
try {
const storage = Object.assign({}, this.get(), {
[key]: value,
})
window.localStorage.setItem(this.name, JSON.stringify(storage))
}
catch {
this.settings[key] = value
}
}
del(key) {
try {
const storage = this.get();
delete storage[key];
window.localStorage.setItem(this.name, JSON.stringify(storage));
// eslint-disable-next-line no-unused-vars
} catch (error) {
delete this.settings[key];
}
del(key) {
try {
const storage = this.get()
delete storage[key]
window.localStorage.setItem(this.name, JSON.stringify(storage))
}
catch {
delete this.settings[key]
}
}
clear() {
try {
window.localStorage.removeItem(this.name);
// eslint-disable-next-line no-unused-vars
} catch (error) {
this.settings = {};
}
clear() {
try {
window.localStorage.removeItem(this.name)
}
catch {
this.settings = {}
}
}
}
Loaded 100 of 447 files, more files were not shown because too many files have changed in this diff. Show more