From 8259195ecaf5abc7d2d1b60e78f0aea8451a4e24 Mon Sep 17 00:00:00 2001 From: chenghuan Date: Tue, 10 Mar 2026 15:17:56 +0800 Subject: [PATCH] =?UTF-8?q?fix:=201.=20=E4=BF=AE=E5=A4=8D=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=92=AD=E6=94=BE=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=8F=8A=E4=BC=98=E5=8C=96=E6=92=AD=E6=94=BE=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=EF=BC=9B2.=E5=B0=8F=E7=8F=AD=E6=9F=A5=E7=9C=8B=E5=BF=83?= =?UTF-8?q?=E5=BE=97=EF=BC=9B3.=20=E8=A7=82=E7=9C=8B=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=88=97=E8=A1=A8=E7=8A=B6=E6=80=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E4=B8=BA=E5=B7=B2=E5=AD=A6=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/requestConfig.js | 2 +- manifest.json | 4 +- package-lock.json | 16 +- package.json | 2 +- pages/course/chapterDetailAndorid.vue | 2 +- pages/course/courseDetail.vue | 10 +- pages/medicaldes/medicaldes.vue | 2 +- pages/miniClass/classInfo.vue | 8 +- .../components/yb-video/yb-video.vue | 6 +- .../static/html/dist/yb-player.js | 210 +++++++++++++++++- 10 files changed, 229 insertions(+), 33 deletions(-) diff --git a/config/requestConfig.js b/config/requestConfig.js index 40abb66..b2eaa93 100644 --- a/config/requestConfig.js +++ b/config/requestConfig.js @@ -300,7 +300,7 @@ $http.dataFactory = async function(res) { data: res.data }); } else { //其他错误提示 - console.log(httpData, '其他') + console.log(httpData, '其他', res.url) console.log(httpData.info || httpData.msg) if (res.isPrompt && res.data.loadAnimate != 'none') { uni.showToast({ diff --git a/manifest.json b/manifest.json index 5c6d47d..24389d3 100644 --- a/manifest.json +++ b/manifest.json @@ -12,8 +12,8 @@ "src" : "图片路径" } ], - "versionName" : "2.0.42", - "versionCode" : 2042, + "versionName" : "2.0.43", + "versionCode" : 2043, "sassImplementationName" : "node-sass", "app-plus" : { "nvueCompiler" : "uni-app", diff --git a/package-lock.json b/package-lock.json index 674add9..ef386cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "animate.css": "^4.1.1", "e-peanut": "file:", - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.8", + "edu-core": "file:../edu-core", "element-plus": "^2.9.6", "epubjs": "^0.3.93", "jquery": "^2.2.4", @@ -25,7 +25,6 @@ }, "../edu-core": { "version": "1.0.7", - "extraneous": true, "license": "ISC", "devDependencies": {} }, @@ -441,9 +440,8 @@ "link": true }, "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/element-plus": { "version": "2.11.5", @@ -4179,7 +4177,7 @@ "requires": { "animate.css": "^4.1.1", "e-peanut": "file:", - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.8", + "edu-core": "file:../edu-core", "element-plus": "^2.9.6", "epubjs": "^0.3.93", "jquery": "^2.2.4", @@ -4500,8 +4498,7 @@ } }, "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" }, "element-plus": { "version": "2.11.5", @@ -7262,8 +7259,7 @@ } }, "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" }, "element-plus": { "version": "2.11.5", diff --git a/package.json b/package.json index 2ea7dbc..9f87094 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dependencies": { "animate.css": "^4.1.1", "e-peanut": "file:", - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.8", + "edu-core": "file:../edu-core", "element-plus": "^2.9.6", "epubjs": "^0.3.93", "jquery": "^2.2.4", diff --git a/pages/course/chapterDetailAndorid.vue b/pages/course/chapterDetailAndorid.vue index 0e9c8d8..1b43b66 100644 --- a/pages/course/chapterDetailAndorid.vue +++ b/pages/course/chapterDetailAndorid.vue @@ -60,7 +60,7 @@ 我的见解 @{{classList[0].title}} - + 修改 diff --git a/pages/course/courseDetail.vue b/pages/course/courseDetail.vue index f8ecc6a..e69eb90 100644 --- a/pages/course/courseDetail.vue +++ b/pages/course/courseDetail.vue @@ -435,7 +435,7 @@ - @@ -653,8 +653,12 @@ // #endif await this.request() - this.$nextTick(()=>{ - this.getData(this.courseId); + this.$nextTick(async ()=>{ + await this.getData(this.courseId); + this.chapterList = await this.getChapterList(this.catalogueId); + // if (this.librayList.length > 0 && this.curIndex !== undefined) { + // await this.clicklib(this.librayList[this.curIndex], this.curIndex); + // } }) }, onUnload() { diff --git a/pages/medicaldes/medicaldes.vue b/pages/medicaldes/medicaldes.vue index cc57420..eaae000 100644 --- a/pages/medicaldes/medicaldes.vue +++ b/pages/medicaldes/medicaldes.vue @@ -2,7 +2,7 @@ - + diff --git a/pages/miniClass/classInfo.vue b/pages/miniClass/classInfo.vue index 8072250..034efdb 100644 --- a/pages/miniClass/classInfo.vue +++ b/pages/miniClass/classInfo.vue @@ -1470,10 +1470,10 @@ // 点击作业或者医案,进入详细介绍 clickTask(item,index) { // var options = JSON.stringify(item) - if(this.thisClass.state != '1'){ - this.$commonJS.showToast("当前班级状态不可提交"); - return - } + // if(this.thisClass.state != '1'){ + // this.$commonJS.showToast("当前班级状态不可提交"); + // return + // } if (this.tijiaoTitleId == 2) { // 进入的是心得 uni.navigateTo({ 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\t\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() {