Просмотр исходного кода

已付费解锁视频进入直播间立即开播

蒋开心 2 недель назад
Родитель
Сommit
cb1b360f02
4 измененных файлов с 108 добавлено и 52 удалено
  1. 2 2
      common/config/runtime-manifest.js
  2. 17 5
      features/live/gateway.js
  3. 2 2
      manifest.json
  4. 87 43
      pages/live/watch.nvue

+ 2 - 2
common/config/runtime-manifest.js

@@ -12,8 +12,8 @@ const ENDPOINTS = Object.freeze(
 )
 
 const IDENTITY = Object.freeze({
-	releaseLabel: '1.1.38',
-	numericBuild: '1138',
+	releaseLabel: '1.1.39',
+	numericBuild: '1139',
 	// 排查"看不到直播"期间临时开启,正式发布请改回 false
 	traceVerbose: false,
 	geoServiceKey: '',

+ 17 - 5
features/live/gateway.js

@@ -82,11 +82,23 @@ export async function probeBroadcastAlive(roomId) {
 }
 
 export async function pullTrialSeconds(roomId) {
-	const { data } = await get(live('getLastTime'), { id: roomId })
-	const nested = data?.data?.last_time
-	const flat = data?.last_time
-	const raw = nested !== undefined ? nested : flat
-	return typeof raw === 'string' ? Number(raw) : raw
+	const { data, raw } = await get(live('getLastTime'), { id: roomId })
+	const candidates = [
+		data != null && typeof data === 'object' ? data.data?.last_time : undefined,
+		data != null && typeof data === 'object' ? data.last_time : undefined,
+		typeof data === 'number' || typeof data === 'string' ? data : undefined,
+		raw?.res?.data?.last_time,
+		raw?.res?.data?.data?.last_time,
+		raw?.data?.last_time,
+		raw?.data?.data?.last_time
+	]
+	for (let i = 0; i < candidates.length; i++) {
+		const rawVal = candidates[i]
+		if (rawVal === undefined || rawVal === null || rawVal === '') continue
+		const n = Number(rawVal)
+		if (!Number.isNaN(n)) return n
+	}
+	return null
 }
 
 /**

+ 2 - 2
manifest.json

@@ -2,8 +2,8 @@
     "name" : "88live",
     "appid" : "__UNI__3F63D9A",
     "description" : "",
-    "versionName" : "1.1.38",
-    "versionCode" : 1038,
+    "versionName" : "1.1.39",
+    "versionCode" : 1039,
     "transformPx" : false,
     /* 5+App特有相关 */
     "app-plus" : {

+ 87 - 43
pages/live/watch.nvue

@@ -519,11 +519,11 @@ export default {
 		shouldRenderPlayer() {
 			// 参考 live-room.nvue:
 			//   v-if="(state == 1 || (state == 2 && playUrl)) && !isPlayStopped"
-			// 直播中即使 playUrl 暂时为空也要把容器挂上,让 <video> 后续能感知 src 变化;
-			// 回放必须有 url;付费遮罩存在时不渲染播放器。
+			// 付费遮罩 / 尚无拉流地址时不挂播放器,避免空壳盖住封面变成黑屏
 			if (this.paywallOn) return false
+			if (!this.streamSrc) return false
 			if (this.sessionPhase === 'onair') return true
-			if (this.sessionPhase === 'replay' && !!this.streamSrc) return true
+			if (this.sessionPhase === 'replay') return true
 			return false
 		},
 		pagerEnabled() {
@@ -1041,6 +1041,11 @@ export default {
 			this.paywallOn = false
 			this.paywallCoverReady = false
 			this.seedPosterFromFeed()
+			// 与详情并行拉取试看秒数,付费房据此决定锁屏 / 开播(-1=无限)
+			const trialPromise = pullTrialSeconds(this.roomKey).catch((err) => {
+				if (appSettings.debug) console.warn('[live] pullTrialSeconds failed', err)
+				return null
+			})
 			let detail = null
 			try {
 				detail = await pullBroadcastDetail(this.roomKey)
@@ -1094,18 +1099,9 @@ export default {
 			console.log('[LIVE-DIAG] platform =', this.platformLabel)
 
 			this.streamArchive = url
-			const paidAmount = Number(detail && detail.paid_money) || 0
-			// 付费未解锁:进房立刻锁屏,避免先出画面再等 getLastTime 才遮罩
-			if (paidAmount > 0 && !this.unlocked) {
-				this.paywallCoverReady = false
-				this.paywallOn = true
-				this.streamSrc = ''
-			} else {
-				this.paywallOn = false
-				this.streamSrc = url || ''
-			}
-			// 强制让 <stream-player> 重新挂载,等价源代码 onShow 里 `playComponentKey = Date.now()`,
-			// 解决 nvue + weex video 在 src 由空变非空时偶发不自动 reload 的问题。
+			// 先不拉流:等 getLastTime,避免已解锁闪锁 / 未解锁先裸播
+			this.streamSrc = ''
+			this.paywallOn = false
 			this.playerRenderToken += 1
 			this.lastDetailSnap = {
 				state: detail && detail.state,
@@ -1115,12 +1111,29 @@ export default {
 				src: this.streamSrc
 			}
 			console.log('=== [LIVE-DIAG] banner snap ===', this.lastDetailSnap)
-			console.log('=== [LIVE-DIAG] shouldRenderPlayer =', this.shouldRenderPlayer, ' paywallOn =', this.paywallOn)
 
-			// 试看检测不阻塞进房;付费房已先锁屏,有剩余试看时再放开
-			this.setupTrial().catch((err) => {
-				console.warn('[LIVE-DIAG] setupTrial failed', err)
-			})
+			const paidAmount = Number(detail && detail.paid_money) || 0
+			try {
+				const sec = await trialPromise
+				console.log('[LIVE-DIAG] getLastTime last_time =', sec, ' paid_money =', paidAmount)
+				this.applyTrialSeconds(sec, { paidAmount })
+			} catch (err) {
+				console.warn('[LIVE-DIAG] applyTrialSeconds failed', err)
+				if (paidAmount > 0 && !this.unlocked) {
+					this.triggerPaywall()
+				} else {
+					this.releasePaywallForTrial()
+				}
+			}
+			console.log(
+				'=== [LIVE-DIAG] shouldRenderPlayer =',
+				this.shouldRenderPlayer,
+				' paywallOn =',
+				this.paywallOn,
+				' streamSrc=',
+				!!this.streamSrc
+			)
+
 			this.resetGiftUiState()
 			this.$nextTick(() => {
 				this.$refs.chatPanel?.reset?.()
@@ -1163,48 +1176,79 @@ export default {
 			} catch (_) {}
 		},
 		async setupTrial() {
-			if (this.unlocked) return
+			if (this.unlocked) {
+				this.releasePaywallForTrial()
+				return
+			}
 			let sec
 			try {
 				sec = await pullTrialSeconds(this.roomKey)
 			} catch (err) {
 				if (appSettings.debug) console.warn('[live] pullTrialSeconds failed', err)
-				// 接口失败:付费房保持/打开锁屏,避免裸播
-				if ((Number(this.session.paid_money) || 0) > 0 && !this.unlocked) {
-					this.triggerPaywall()
-				}
+				sec = null
+			}
+			this.applyTrialSeconds(sec)
+		},
+		/**
+		 * 按 getLastTime 结果处理:
+		 *   -1 => 无限播放(免费或已解锁)
+		 *   >0 => 试看倒计时
+		 *   <=0 => 付费锁屏
+		 */
+		applyTrialSeconds(sec, opts = {}) {
+			const paidAmount =
+				opts.paidAmount != null
+					? Number(opts.paidAmount) || 0
+					: Number(this.session && this.session.paid_money) || 0
+
+			if (this.unlocked) {
+				this.releasePaywallForTrial()
 				return
 			}
-			// -1:免费全场
-			if (sec === -1) {
+
+			let n = sec
+			if (n !== null && n !== undefined && n !== '') {
+				n = Number(n)
+				if (Number.isNaN(n)) n = null
+			} else {
+				n = null
+			}
+
+			this.trialClock?.stop()
+
+			// -1:无限播放
+			if (n === -1) {
+				this.unlocked = true
 				this.releasePaywallForTrial()
 				return
 			}
-			if (sec == null) {
-				if ((Number(this.session.paid_money) || 0) > 0 && !this.unlocked) {
+
+			if (n == null) {
+				if (paidAmount > 0) {
 					this.triggerPaywall()
+				} else {
+					this.releasePaywallForTrial()
 				}
 				return
 			}
-			this.trialClock?.stop()
+
 			this.trialClock = new TrialMeter(() => this.triggerPaywall())
-			if (sec <= 0) {
+			if (n <= 0) {
 				this.triggerPaywall()
 				return
 			}
-			// 仍有试看:关锁屏、开播并倒计时
+			// 仍有试看
 			this.releasePaywallForTrial()
-			this.trialClock.start(sec)
+			this.trialClock.start(n)
 		},
-		/** 有试看时长时放开锁屏并恢复拉流 */
+		/** 关锁屏并恢复拉流(强制 remount,避免 nvue 空 src 挂载后黑屏) */
 		releasePaywallForTrial() {
-			if (this.unlocked) return
 			this.paywallOn = false
-			const url = this.streamArchive || resolvePlaybackUrl(this.session)
-			if (!url) return
-			if (this.streamSrc !== url) {
-				this.streamSrc = url
-				this.playerRenderToken += 1
+			const url = this.streamArchive || resolvePlaybackUrl(this.session) || ''
+			this.streamSrc = url
+			this.playerRenderToken += 1
+			if (!url) {
+				console.warn('[LIVE-DIAG] releasePaywallForTrial: empty stream url')
 			}
 		},
 		triggerPaywall() {
@@ -1351,15 +1395,15 @@ export default {
 		},
 		/** 解锁成功后恢复播放(本工程不整页 redirect) */
 		resumeAfterUnlock() {
-			this.unlocked = true
-			this.paywallOn = false
+					this.unlocked = true
+					this.paywallOn = false
 			this.showLivePayPopup = false
 			this.showInsufficientBalanceModal = false
 			this.trialClock?.stop()
 			const url = this.streamArchive || resolvePlaybackUrl(this.session)
 			this.streamSrc = url || ''
 			this.playerRenderToken += 1
-			this.sessionPhase = describeRoomPhase(this.session.state)
+					this.sessionPhase = describeRoomPhase(this.session.state)
 		},
 		/** @deprecated 保留旧入口名,避免残留引用 */
 		confirmUnlock() {