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

+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)
},
})
}