diff --git a/manifest.json b/manifest.json index 6f2c808..b91c2c7 100644 --- a/manifest.json +++ b/manifest.json @@ -13,8 +13,8 @@ "src" : "图片路径" } ], - "versionName" : "1.0.55", - "versionCode" : 1055, + "versionName" : "1.0.56", + "versionCode" : 1056, "app-plus" : { "nvueCompiler" : "weex", "compatible" : { diff --git a/package-lock.json b/package-lock.json index 1cc7930..ce735ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.4.5", "license": "MIT", "dependencies": { - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.8", + "edu-core": "file:../edu-core", "jquery": "^3.7.1", "tcplayer.js": "^5.1.0" }, @@ -19,7 +19,6 @@ }, "../edu-core": { "version": "1.0.8", - "extraneous": true, "license": "ISC", "devDependencies": {} }, @@ -74,9 +73,8 @@ "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, "node_modules/edu-core": { - "version": "1.0.8", - "resolved": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#a5f40fafc3935e886a3021b3a9ef49ed7ac13025", - "license": "ISC" + "resolved": "../edu-core", + "link": true }, "node_modules/es5-shim": { "version": "4.6.7", @@ -399,8 +397,7 @@ "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, "edu-core": { - "version": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#a5f40fafc3935e886a3021b3a9ef49ed7ac13025", - "from": "edu-core@git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.8" + "version": "file:../edu-core" }, "es5-shim": { "version": "4.6.7", diff --git a/package.json b/package.json index f2ec4d0..87741e5 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/dcloudio/hello-uniapp#readme", "dependencies": { - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.8", + "edu-core": "file:../edu-core", "jquery": "^3.7.1", "tcplayer.js": "^5.1.0" }, diff --git a/uni_modules/yingbing-video/components/yb-video/yb-video.vue b/uni_modules/yingbing-video/components/yb-video/yb-video.vue index c4a22fe..47ddf7d 100644 --- a/uni_modules/yingbing-video/components/yb-video/yb-video.vue +++ b/uni_modules/yingbing-video/components/yb-video/yb-video.vue @@ -110,6 +110,11 @@ type: Array, default: () => [] }, + //是否显示倍速提示 + showRateTips: { + type: Boolean, + default: false + }, //循环播放 loop: { type: Boolean, @@ -260,7 +265,6 @@ created() { //获取当前运行的平台 this.updateHeight() - console.log('倍速列表是否是ios YINGBING' + JSON.stringify(this.playbackRates)) }, methods: { //接收消息 diff --git a/uni_modules/yingbing-video/static/html/dist/yb-player.js b/uni_modules/yingbing-video/static/html/dist/yb-player.js index 7b4f13a..13481dc 100644 --- a/uni_modules/yingbing-video/static/html/dist/yb-player.js +++ b/uni_modules/yingbing-video/static/html/dist/yb-player.js @@ -65,13 +65,18 @@ var YbPlayer = /*#__PURE__*/function () { this.loop = loop; //是否循环播放 this.muted = muted; //是否静音 this.playbackRate = playbackRate || 1; //默认播放倍速 - console.log('倍速列表' + JSON.stringify(playbackRates)) this.playbackRates = playbackRates && playbackRates.length > 0 ? playbackRates : [{ text: '0.5倍速', value: 0.5 + }, { + text: '0.75倍速', + value: 0.75 }, { text: '正常倍速', value: 1 + }, { + text: '1.25倍速', + value: 1.25 }, { text: '1.5倍速', value: 1.5 @@ -120,6 +125,9 @@ var YbPlayer = /*#__PURE__*/function () { this._toastTimer = null; //消息隐藏定时器 this._danmuTimer = null; //弹幕定时器 this._seizingTimer = null; //卡死定时器(播放一些直播源的时候,可能会出现卡死无反应的情况,需要做出处理) + this._rateTimer = null; //倍速显示定时器 + this._errorRetryCount = 0; //错误重试次数 + this._maxErrorRetry = 0; //最大重试次数 this._event = {}; } //开启全屏按钮 @@ -157,6 +165,7 @@ var YbPlayer = /*#__PURE__*/function () { this._clearDanmuTimer(); this._clearToastTimer(); this._clearControlsTimer(); + this._clearRateTimer(); this._removeBackbuttonListener(); this._event = {}; //卸载所有监听事件 if (this.container) { @@ -226,8 +235,8 @@ var YbPlayer = /*#__PURE__*/function () { _this2.emit('durationchange', _this2.getDuration()); }; this.video.onloadeddata = function () { - //非直播时初始化播放时长 - if (_this2.initialTime && !_this2.isLive) _this2.seek(_this2.initialTime); + //非直播时初始化播放时长(错误恢复时不执行) + if (_this2.initialTime && !_this2.isLive && !_this2._isErrorRecovering) _this2.seek(_this2.initialTime); _this2.emit('loadeddata', { duration: _this2.getDuration(), videoWidth: _this2.video.videoWidth, @@ -255,12 +264,18 @@ var YbPlayer = /*#__PURE__*/function () { if (_this2.cm) _this2.cm.setConfig('playbackRate', playbackRate); _this2.emit('ratechange', playbackRate); _this2.setInnerHTML('yb-player-header-rate', '倍速x' + playbackRate); + _this2._clearRateTimer(); var rateEl = _this2.container.getElementsByClassName('yb-player-rate')[0] || document.createElement('DIV'); if (![1, 1.0].includes(playbackRate)) { rateEl.setAttribute('class', 'yb-player-rate'); rateEl.innerHTML = "\n\t\t\t\t\t
\n\t\t\t\t\t".concat(playbackRate + '倍速播放中', "\n\t\t\t\t"); var wrapperEl = _this2.container.getElementsByClassName('yb-player-wrapper')[0]; if (wrapperEl) wrapperEl.appendChild(rateEl); + _this2._rateTimer = window.setTimeout(function () { + if (rateEl && rateEl.parentNode) { + rateEl.parentNode.removeChild(rateEl); + } + }, 2000); } else { if (rateEl) rateEl.remove(); } @@ -353,7 +368,6 @@ var YbPlayer = /*#__PURE__*/function () { }; this.video.onerror = function (e) { if (e && e.target.error) { - // 网络问题或其他不可恢复的错误 var code = e.target.error.code; var errorMsg = ''; switch (code) { @@ -370,10 +384,26 @@ var YbPlayer = /*#__PURE__*/function () { errorMsg = '视频源不支持或地址无效'; break; } - _this2.showError(errorMsg); - _this2.unloadVideo(); + console.log('[video.onerror] 检测到视频错误,错误码:' + code + ', 错误信息:' + errorMsg); + + // 错误码 3 尝试跳过问题区间后继续播放 + if (code === 3) { + _this2._skipErrorAndRetry(errorMsg); + } else { + // 其他错误直接提示错误信息 + _this2.showError(errorMsg); + _this2.unloadVideo(); + } + + // emit 错误事件,传递详细的错误信息 + _this2.emit('error', { + code: code, + message: errorMsg, + error: e + }); + } else { + _this2.emit('error', e); } - _this2.emit('error', e); }; //视频长按菜单取消 this.video.oncontextmenu = function (e) { @@ -410,6 +440,7 @@ var YbPlayer = /*#__PURE__*/function () { key: "unloadVideo", value: function unloadVideo() { this._clearSeizingTimer(); + this._errorRetryCount = 0; // 重置错误计数 this.unloadCustom(); this.unloadDecoder(); this.unloadPano(); @@ -1430,7 +1461,7 @@ var YbPlayer = /*#__PURE__*/function () { if (['timeDiffrence', 'disableScroll', 'disableTop', 'disableBottom', 'fontScale'].includes(key)) _this.cm.reset(); } _this.emit('danmuconfigchange', config); //派发弹幕配置更改事件,以便开发者外部记录 - }, 500); + }, 2000); } this.showPopup('弹幕设置', div); } @@ -2451,7 +2482,14 @@ var YbPlayer = /*#__PURE__*/function () { value: function hideLoading() { var div = this.container.getElementsByClassName('yb-player-loading')[0]; if (div) div.remove(); - if (this.video.paused) this.showCenterPlay();else this.hideCenter(); + // if (this.video.paused) this.showCenterPlay();else this.hideCenter(); + if (this._isErrorRecovering) { + this.hideCenter(); + } else if (this.video.paused) { + this.showCenterPlay(); + } else { + this.hideCenter(); + } } //展示中间播放按钮 }, { @@ -2561,6 +2599,152 @@ var YbPlayer = /*#__PURE__*/function () { value: function seek(time) { if (this.video) this.video.currentTime = time; } + /** + * 跳过错误区间并重试播放 + * @param {String} errorMsg 错误信息 + */ + }, { + key: "_skipErrorAndRetry", + value: function _skipErrorAndRetry(errorMsg) { + var _this = this; + + // 检查 video 对象是否存在 + if (!this.video) { + console.log('[错误恢复] video 对象不存在,无法恢复'); + this.showError(errorMsg); + this.unloadVideo(); + return; + } + + // 超过最大重试次数,显示错误并卸载视频 + if (this._errorRetryCount >= this._maxErrorRetry) { + this.showError(errorMsg); + this.unloadVideo(); + return; + } + + this._errorRetryCount++; + + // 第 1 次错误静默处理,显示 loading + if (this._errorRetryCount === 1) { + this.showLoading(); + } + + // 获取当前播放时间,向后跳过 1 秒(跳过损坏的 GOP 区间) + var currentTime = this.video.currentTime || 0; + // 确保 currentTime 是有效数字 + if (isNaN(currentTime) || currentTime < 0) { + currentTime = 0; + } + var duration = this.getDuration() || 0; + var skipTime = Math.min(currentTime + 1, duration); + + console.log('[错误恢复] 开始处理,当前时间:' + currentTime + 's, 跳转到:' + skipTime + 's'); + + // 关键:保存当前 src,然后重新加载视频来恢复状态 + // var currentSrc = this.video.currentSrc || this.video.src; + console.log('[错误恢复] 重新加载视频...'); + + // 保存当前倍速设置 + var currentPlaybackRate = this.video.playbackRate || 1; + console.log('[错误恢复] 保存倍速:' + currentPlaybackRate); + + // 设置标志,告诉 loadeddata 不要执行 seek + this._isErrorRecovering = true; + + // 重新设置 src 并加载 + // this.video.src = currentSrc; + this.video.load(); + + // 清除之前的定时器(如果有) + if (this._errorRecoveryTimer) { + clearTimeout(this._errorRecoveryTimer); + } + + // 等待元数据加载完成 + var onLoadedMetadata = function() { + // 检查 video 对象是否仍然存在 + if (!_this.video) { + console.log('[错误恢复] loadedmetadata 触发,但 video 对象已不存在'); + return; + } + + console.log('[错误恢复] loadedmetadata 触发,设置 currentTime = ' + skipTime); + _this.video.removeEventListener('loadedmetadata', onLoadedMetadata); + + // 清除超时定时器 + if (_this._errorRecoveryTimer) { + clearTimeout(_this._errorRecoveryTimer); + } + + // 恢复倍速设置 + _this.video.playbackRate = currentPlaybackRate; + console.log('[错误恢复] 恢复倍速:' + currentPlaybackRate); + + // 设置跳转位置 + _this.video.currentTime = skipTime; + + // 等待 seek 完成后立即播放 + var onSeeked = function() { + // 检查 video 对象是否仍然存在 + if (!_this2.video) { + console.log('[错误恢复] seeked 触发,但 video 对象已不存在'); + return; + } + + console.log('[错误恢复] seeked 触发,实际位置:' + _this2.video.currentTime); + _this2.video.removeEventListener('seeked', onSeeked); + _this2.tryPlayAfterSeek(errorMsg); + }; + + _this.video.addEventListener('seeked', onSeeked); + }; + + this.video.addEventListener('loadedmetadata', onLoadedMetadata); + + // loadedmetadata 超时处理 + this._errorRecoveryTimer = setTimeout(function() { + console.log('[错误恢复] loadedmetadata 超时,直接尝试播放'); + if (_this.video) { + _this.video.removeEventListener('loadedmetadata', onLoadedMetadata); + } + _this.tryPlayAfterSeek(errorMsg); + }, 5000); + } + + // 尝试播放 + }, { + key: "tryPlayAfterSeek", + value: function tryPlayAfterSeek(errorMsg) { + var _this2 = this; + + console.log('[错误恢复] 准备播放,当前状态:paused=' + this.video.paused + ', readyState=' + this.video.readyState + ', currentTime=' + this.video.currentTime); + + // 直接调用 play(),Promise 会处理缓冲 + var playPromise = this.video.play(); + console.log('[错误恢复] play() 已调用'); + + if (playPromise && typeof playPromise.then === 'function') { + playPromise.then(function() { + console.log('[错误恢复] 播放成功!'); + _this2._errorRetryCount = 0; + _this2._isErrorRecovering = false; // 清除错误恢复标志 + // 清除所有定时器 + if (_this2._errorRecoveryTimer) { + clearTimeout(_this2._errorRecoveryTimer); + _this2._errorRecoveryTimer = null; + } + _this2.hideLoading(); + }).catch(function(err) { + console.log('[错误恢复] 播放失败:', err); + _this2._skipErrorAndRetry(errorMsg); + }); + } else { + _this2._errorRetryCount = 0; + _this2._isErrorRecovering = false; + _this2.hideLoading(); + } + } //播放上一个视频 }, { key: "prev", @@ -2962,6 +3146,14 @@ var YbPlayer = /*#__PURE__*/function () { this._seizingTimer = null; } } + }, { + key: "_clearRateTimer", + value: function _clearRateTimer() { + if (this._rateTimer) { + window.clearTimeout(this._rateTimer); + this._rateTimer = null; + } + } }, { key: "_removeBackbuttonListener", value: function _removeBackbuttonListener() {