bugfix
This commit is contained in:
parent
6384917c61
commit
48640cd124
@ -10,7 +10,7 @@ SOURCES=" page_data_loader.js factory.js uitypesdef.js utils.js uitype.js \
|
|||||||
llm_dialog.js llm.js websocket.js datarow.js tabular.js continueaudio.js \
|
llm_dialog.js llm.js websocket.js datarow.js tabular.js continueaudio.js \
|
||||||
line.js pie.js bar.js gobang.js period.js iconbarpage.js \
|
line.js pie.js bar.js gobang.js period.js iconbarpage.js \
|
||||||
keypress.js asr.js webspeech.js countdown.js progressbar.js \
|
keypress.js asr.js webspeech.js countdown.js progressbar.js \
|
||||||
qaframe.js svg.js "
|
qaframe.js svg.js videoplayer.js "
|
||||||
echo ${SOURCES}
|
echo ${SOURCES}
|
||||||
cat ${SOURCES} > ../dist/bricks.js
|
cat ${SOURCES} > ../dist/bricks.js
|
||||||
# uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js
|
# uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js
|
||||||
|
|||||||
@ -506,3 +506,71 @@ hr {
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
position: relative;
|
||||||
|
width: 80%;
|
||||||
|
max-width: 1000px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-element {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 10px 15px;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
accent-color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls-bottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-pause, .mute, .fullscreen {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume, .playback-speed, .audio-track-select {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|||||||
@ -705,6 +705,30 @@ function blobToBase64(blob) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
opts = {
|
||||||
|
css:
|
||||||
|
id
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
bricks.dom_create(tag, opts){
|
||||||
|
var e = document.createElement(tag);
|
||||||
|
if (opts.css){
|
||||||
|
var arr = css.split(' ');
|
||||||
|
arr.forEach(c =>{
|
||||||
|
e.classList.add(c);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (opts.id){
|
||||||
|
e.id = opts.id;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
bricks.element_from_html(html){
|
||||||
|
var e = document.createElement('div');
|
||||||
|
e.outerHTML = html;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
/*
|
||||||
// 使用队列
|
// 使用队列
|
||||||
const queue = new Queue();
|
const queue = new Queue();
|
||||||
queue.enqueue(1);
|
queue.enqueue(1);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
var bricks = window.bricks || {}
|
var bricks = window.bricks || {}
|
||||||
/*
|
/*
|
||||||
use hls to play m3u8
|
use hls to play m3u8
|
||||||
@ -8,223 +7,72 @@ use dash to play dash
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
bricks VideoPlayer = class extends bricks.VBox {
|
bricks VideoPlayer = class extends bricks.VBox {
|
||||||
|
/*
|
||||||
|
opts:
|
||||||
|
url: video source
|
||||||
|
autoplay:true or false
|
||||||
|
*/
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
super(opts)
|
super(opts)
|
||||||
this.video = videoElement;
|
this.set_css('video-container');
|
||||||
|
this.video = bricks.element_from_html(`<video id="video" class="video-element"></video>`);
|
||||||
|
this.controls = `<div class="controls">
|
||||||
|
<div class="progress-container">
|
||||||
|
<input type="range" class="progress-bar" value="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
<div class="controls-bottom">
|
||||||
|
<button class="play-pause">▶</button>
|
||||||
|
<div class="volume-container">
|
||||||
|
<button class="mute">🔊</button>
|
||||||
|
<input type="range" class="volume" min="0" max="1" step="0.01" value="1" />
|
||||||
|
</div>
|
||||||
|
<span class="time">00:00 / 00:00</span>
|
||||||
|
<div class="speed-container">
|
||||||
|
<select class="playback-speed">
|
||||||
|
<option value="0.5">0.5x</option>
|
||||||
|
<option value="0.75">0.75x</option>
|
||||||
|
<option value="1" selected>1x</option>
|
||||||
|
<option value="1.25">1.25x</option>
|
||||||
|
<option value="1.5">1.5x</option>
|
||||||
|
<option value="2">2x</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="audio-tracks">
|
||||||
|
<select class="audio-track-select"></select>
|
||||||
|
</div>
|
||||||
|
<button class="fullscreen">⛶</button>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
this.dom_element.appendChild(this.video);
|
||||||
|
this.dom_element.appendChild(this.controls);
|
||||||
|
|
||||||
this.hls = null;
|
this.hls = null;
|
||||||
this.dashPlayer = null;
|
this.dashPlayer = null;
|
||||||
|
|
||||||
this.audioTrackSelect = document.getElementById('audioTrackSelect');
|
this.playPauseBtn = this.controls.querySelector('.play-pause');
|
||||||
this.timeDisplay = document.getElementById('timeDisplay');
|
this.muteBtn = this.controls.querySelector('.mute');
|
||||||
/*
|
this.volumeInput = this.controls.querySelector('.volume');
|
||||||
this.initEventListeners();
|
this.progressBar = this.controls.querySelector('.progress-bar');
|
||||||
this.initTrackChangeHandler();
|
this.timeDisplay = this.controls.querySelector('.time');
|
||||||
*/
|
this.speedSelect = this.controls.querySelector('.playback-speed');
|
||||||
}
|
this.audioTrackSelect = this.controls.querySelector('.audio-track-select');
|
||||||
create(){
|
this.fullscreenBtn = this.controls.querySelector('.fullscreen');
|
||||||
this.dom_element = document.createElement('video', {controls: true});
|
|
||||||
}
|
|
||||||
// 播放指定 URL 的视频
|
|
||||||
load(src) {
|
|
||||||
const video = this.video;
|
|
||||||
|
|
||||||
// 销毁之前的播放器实例
|
this.bind('domon', this.init.bind(this));
|
||||||
this.destroy();
|
this.bind('domoff', this.destroy.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
// 判断视频类型
|
init() {
|
||||||
if (Hls.isSupported() && src.endsWith('.m3u8')) {
|
this.loadVideo(this.opts.url); // 可替换为 mp4 / m3u8 / mpd
|
||||||
this.loadHLS(src);
|
this.bindEvents();
|
||||||
} else if (src.endsWith('.mpd')) {
|
this.updateUI();
|
||||||
this.loadDASH(src);
|
if (this.opts.autoplay && this.video.paused){
|
||||||
} else {
|
this.playPauseBtn.click();
|
||||||
// 普通视频(MP4/WebM/Ogg)
|
|
||||||
video.src = src;
|
|
||||||
video.load();
|
|
||||||
this.setupEventHandlers();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadHLS(src) {
|
loadVideo(src) {
|
||||||
this.hls = new Hls();
|
// 销毁旧播放器
|
||||||
this.hls.loadSource(src);
|
|
||||||
this.hls.attachMedia(this.video);
|
|
||||||
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
||||||
this.video.play().catch(e => console.warn("自动播放被阻止", e));
|
|
||||||
this.setupEventHandlers();
|
|
||||||
this.updateAudioTracks();
|
|
||||||
});
|
|
||||||
this.hls.on(Hls.Events.ERROR, (event, data) => {
|
|
||||||
if (data.fatal) {
|
|
||||||
console.error('HLS 错误:', data);
|
|
||||||
if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
|
|
||||||
this.hls.startLoad();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadDASH(src) {
|
|
||||||
this.dashPlayer = dashjs.MediaPlayer().create();
|
|
||||||
this.dashPlayer.initialize(this.video, src, true);
|
|
||||||
this.dashPlayer.on('error', (e) => {
|
|
||||||
console.error('DASH 播放出错:', e);
|
|
||||||
});
|
|
||||||
this.setupEventHandlers();
|
|
||||||
setTimeout(() => this.updateAudioTracks(), 1000); // 延迟获取音轨
|
|
||||||
}
|
|
||||||
|
|
||||||
setupEventHandlers() {
|
|
||||||
const video = this.video;
|
|
||||||
|
|
||||||
video.addEventListener('play', () => this.onPlay());
|
|
||||||
video.addEventListener('pause', () => this.onPause());
|
|
||||||
video.addEventListener('timeupdate', () => this.onTimeUpdate());
|
|
||||||
video.addEventListener('loadedmetadata', () => this.onLoadedMetadata());
|
|
||||||
video.addEventListener('ended', () => this.onEnded());
|
|
||||||
video.addEventListener('error', (e) => this.onError(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件回调
|
|
||||||
onPlay() {
|
|
||||||
console.log('视频开始播放');
|
|
||||||
}
|
|
||||||
|
|
||||||
onPause() {
|
|
||||||
console.log('视频暂停');
|
|
||||||
}
|
|
||||||
|
|
||||||
onTimeUpdate() {
|
|
||||||
const { currentTime, duration } = this.video;
|
|
||||||
this.timeDisplay.textContent = `${this.formatTime(currentTime)} / ${this.formatTime(duration)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoadedMetadata() {
|
|
||||||
console.log('元数据加载完成');
|
|
||||||
this.updateAudioTracks();
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnded() {
|
|
||||||
console.log('视频播放结束');
|
|
||||||
}
|
|
||||||
|
|
||||||
onError(e) {
|
|
||||||
console.error('视频播放错误:', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 音轨切换支持
|
|
||||||
updateAudioTracks() {
|
|
||||||
const select = this.audioTrackSelect;
|
|
||||||
select.innerHTML = ''; // 清空
|
|
||||||
|
|
||||||
const tracks = this.getAudioTracks();
|
|
||||||
|
|
||||||
if (tracks.length === 0) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.textContent = '无可用音轨';
|
|
||||||
option.disabled = true;
|
|
||||||
select.appendChild(option);
|
|
||||||
select.disabled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
select.disabled = false;
|
|
||||||
|
|
||||||
tracks.forEach((track, index) => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = index;
|
|
||||||
option.textContent = track.label || track.language || `音轨 ${index + 1}`;
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 默认选中第一个
|
|
||||||
select.value = 0;
|
|
||||||
this.bindAudioTrackChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
getAudioTracks() {
|
|
||||||
const video = this.video;
|
|
||||||
|
|
||||||
// DASH 情况:使用 dash.js API
|
|
||||||
if (this.dashPlayer) {
|
|
||||||
const audioTracks = this.dashPlayer.getTracksFor('audio');
|
|
||||||
return audioTracks.map(t => ({
|
|
||||||
label: t.label || t.lang,
|
|
||||||
language: t.lang,
|
|
||||||
_track: t
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// HLS 情况:使用 hls.js 的 audio tracks
|
|
||||||
if (this.hls && this.hls.audioTracks.length > 0) {
|
|
||||||
return this.hls.audioTracks.map(track => ({
|
|
||||||
label: track.name || track.lang,
|
|
||||||
language: track.lang,
|
|
||||||
_track: track
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 原生 HTMLTrackElement(较少见,主要用于 <track kind="audio">)
|
|
||||||
const nativeTracks = Array.from(video.audioTracks || []);
|
|
||||||
if (nativeTracks.length > 0) {
|
|
||||||
return nativeTracks.map(track => ({
|
|
||||||
label: track.label || track.language,
|
|
||||||
language: track.language,
|
|
||||||
_track: track
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback:返回主音轨
|
|
||||||
return [{ label: '主音轨', language: 'und' }];
|
|
||||||
}
|
|
||||||
|
|
||||||
bindAudioTrackChange() {
|
|
||||||
this.audioTrackSelect.removeEventListener('change', this.handleAudioTrackChange.bind(this));
|
|
||||||
this.audioTrackSelect.addEventListener('change', this.handleAudioTrackChange.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAudioTrackChange() {
|
|
||||||
const selectedIndex = parseInt(this.audioTrackSelect.value, 10);
|
|
||||||
const tracks = this.getAudioTracks();
|
|
||||||
const selected = tracks[selectedIndex];
|
|
||||||
|
|
||||||
if (this.hls && selected._track && selected._track.id !== undefined) {
|
|
||||||
this.hls.currentLevel = -1; // 确保视频流不变
|
|
||||||
this.hls.audioTrack = selected._track.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dashPlayer && selected._track) {
|
|
||||||
this.dashPlayer.setCurrentTrack(selected._track);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于原生音轨(较少见)
|
|
||||||
if (selected._track && selected._track.enabled !== undefined) {
|
|
||||||
Array.from(this.video.audioTracks).forEach(t => {
|
|
||||||
t.enabled = (t === selected._track);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('切换到音轨:', selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 工具函数:格式化时间
|
|
||||||
formatTime(seconds) {
|
|
||||||
const s = Math.floor(seconds % 60).toString().padStart(2, '0');
|
|
||||||
const m = Math.floor((seconds / 60) % 60).toString().padStart(2, '0');
|
|
||||||
const h = Math.floor(seconds / 3600).toString().padStart(2, '0');
|
|
||||||
return seconds >= 3600 ? `${h}:${m}:${s}` : `${m}:${s}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 播放/暂停控制
|
|
||||||
togglePlay() {
|
|
||||||
if (this.video.paused) {
|
|
||||||
this.video.play().catch(e => console.warn("播放失败:", e));
|
|
||||||
} else {
|
|
||||||
this.video.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 销毁当前播放器实例
|
|
||||||
destroy() {
|
|
||||||
if (this.hls) {
|
if (this.hls) {
|
||||||
this.hls.destroy();
|
this.hls.destroy();
|
||||||
this.hls = null;
|
this.hls = null;
|
||||||
@ -233,25 +81,152 @@ bricks VideoPlayer = class extends bricks.VBox {
|
|||||||
this.dashPlayer.reset();
|
this.dashPlayer.reset();
|
||||||
this.dashPlayer = null;
|
this.dashPlayer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.video.src = ''; // 清空
|
||||||
|
|
||||||
|
if (src.endsWith('.m3u8') || src.includes('m3u8')) {
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
this.hls = new Hls();
|
||||||
|
this.hls.loadSource(src);
|
||||||
|
this.hls.attachMedia(this.video);
|
||||||
|
this.hls.on(Hls.Events.MANIFEST_PARSED, () => this.onLoaded());
|
||||||
|
} else {
|
||||||
|
console.error('HLS not supported');
|
||||||
|
}
|
||||||
|
} else if (src.endsWith('.mpd') || src.includes('mpd')) {
|
||||||
|
this.dashPlayer = dashjs.MediaPlayer().create();
|
||||||
|
this.dashPlayer.initialize(this.video, src, true);
|
||||||
|
this.dashPlayer.on('manifestParsed', () => this.onLoaded());
|
||||||
|
} else {
|
||||||
|
// 普通视频
|
||||||
|
this.video.src = src;
|
||||||
|
this.video.addEventListener('loadedmetadata', () => this.onLoaded());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化播放器
|
onLoaded() {
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
this.updateAudioTracks();
|
||||||
const video = document.getElementById('videoPlayer');
|
this.updateUI();
|
||||||
const player = new VideoPlayer(video);
|
}
|
||||||
|
|
||||||
// 绑定播放/暂停按钮
|
bindEvents() {
|
||||||
document.getElementById('playPause').addEventListener('click', () => {
|
// 播放/暂停
|
||||||
player.togglePlay();
|
this.playPauseBtn.addEventListener('click', () => {
|
||||||
|
if (this.video.paused) {
|
||||||
|
this.video.play();
|
||||||
|
} else {
|
||||||
|
this.video.pause();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 示例:自动加载一个 m3u8 视频(替换为你的实际链接)
|
// 静音切换
|
||||||
// player.load('https://example.com/stream.m3u8');
|
this.muteBtn.addEventListener('click', () => {
|
||||||
|
this.video.muted = !this.video.muted;
|
||||||
|
this.updateMuteUI();
|
||||||
|
});
|
||||||
|
|
||||||
// 或者加载 DASH
|
// 音量变化
|
||||||
// player.load('https://example.com/stream.mpd');
|
this.volumeInput.addEventListener('input', (e) => {
|
||||||
|
this.video.volume = e.target.value;
|
||||||
|
this.video.muted = this.video.volume === 0;
|
||||||
|
this.updateMuteUI();
|
||||||
|
});
|
||||||
|
|
||||||
// 或者普通 MP4
|
// 进度条拖动
|
||||||
player.load('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4');
|
this.progressBar.addEventListener('input', (e) => {
|
||||||
});
|
const time = e.target.value * this.video.duration;
|
||||||
|
this.video.currentTime = time;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 播放速度
|
||||||
|
this.speedSelect.addEventListener('change', (e) => {
|
||||||
|
this.video.playbackRate = parseFloat(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 音轨切换
|
||||||
|
this.audioTrackSelect.addEventListener('change', (e) => {
|
||||||
|
const index = parseInt(e.target.value);
|
||||||
|
if (this.video.audioTracks) {
|
||||||
|
for (let i = 0; i < this.video.audioTracks.length; i++) {
|
||||||
|
this.video.audioTracks[i].enabled = i === index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 全屏
|
||||||
|
this.fullscreenBtn.addEventListener('click', () => {
|
||||||
|
if (this.dom_element.requestFullscreen) {
|
||||||
|
this.dom_element.requestFullscreen();
|
||||||
|
} else if (this.dom_element.webkitRequestFullscreen) {
|
||||||
|
this.dom_element.webkitRequestFullscreen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 视频事件
|
||||||
|
this.video.addEventListener('play', () => this.updatePlayPauseUI());
|
||||||
|
this.video.addEventListener('pause', () => this.updatePlayPauseUI());
|
||||||
|
this.video.addEventListener('timeupdate', () => this.updateProgress());
|
||||||
|
this.video.addEventListener('durationchange', () => this.updateProgress());
|
||||||
|
this.video.addEventListener('volumechange', () => {
|
||||||
|
this.updateMuteUI();
|
||||||
|
this.volumeInput.value = this.video.volume;
|
||||||
|
});
|
||||||
|
this.video.addEventListener('loadedmetadata', () => {
|
||||||
|
this.updateAudioTracks();
|
||||||
|
});
|
||||||
|
this.video.addEventListener('seeking', () => {
|
||||||
|
this.progressBar.value = this.video.currentTime / this.video.duration;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUI() {
|
||||||
|
this.updatePlayPauseUI();
|
||||||
|
this.updateMuteUI();
|
||||||
|
this.updateProgress();
|
||||||
|
this.volumeInput.value = this.video.volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePlayPauseUI() {
|
||||||
|
this.playPauseBtn.textContent = this.video.paused ? '▶' : '❚❚';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMuteUI() {
|
||||||
|
this.muteBtn.textContent = this.video.muted || this.video.volume === 0 ? '🔇' : '🔊';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress() {
|
||||||
|
const percent = this.video.duration ? this.video.currentTime / this.video.duration : 0;
|
||||||
|
this.progressBar.value = percent;
|
||||||
|
this.timeDisplay.textContent = `${this.formatTime(this.video.currentTime)} / ${this.formatTime(this.video.duration || 0)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAudioTracks() {
|
||||||
|
this.audioTrackSelect.innerHTML = '';
|
||||||
|
if (this.video.audioTracks && this.video.audioTracks.length > 0) {
|
||||||
|
for (let i = 0; i < this.video.audioTracks.length; i++) {
|
||||||
|
const track = this.video.audioTracks[i];
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = i;
|
||||||
|
option.textContent = track.label || `音轨 ${i + 1}`;
|
||||||
|
if (track.enabled) option.selected = true;
|
||||||
|
this.audioTrackSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.textContent = '无音轨';
|
||||||
|
option.disabled = true;
|
||||||
|
this.audioTrackSelect.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatTime(seconds) {
|
||||||
|
const s = Math.floor(seconds % 60);
|
||||||
|
const m = Math.floor((seconds / 60) % 60);
|
||||||
|
const h = Math.floor(seconds / 3600);
|
||||||
|
return h > 0
|
||||||
|
? `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
|
||||||
|
: `${m}:${s.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
bricks.Factory.register('VideoPlayer', bricks.VideoPlayer);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user