<template>
	<view class="player-container">
		<view class="header">
			<view class="back-btn" @click="goBack">← 返回</view>
			<text class="title">{{ videoTitle }}</text>
			<view class="placeholder"></view>
		</view>
		
		<!-- 错误提示 -->
		<view v-if="videoError" class="error-overlay">
			<view class="error-content">
				<view class="error-icon">⚠️</view>
				<text class="error-title">视频加载失败</text>
				<text class="error-message">{{ errorMessage }}</text>
				<view class="error-actions">
					<view class="error-btn retry-btn" @click="retryVideo">重新加载</view>
					<view class="error-btn back-btn-error" @click="goBack">返回</view>
				</view>
			</view>
		</view>
		
		<!-- 加载提示 -->
		<view v-if="showLoadingOverlay && !videoError" class="loading-overlay" @click="handleLoadingOverlayClick">
			<view class="loading-content">
				<view class="loading-spinner"></view>
				<text class="loading-text">{{ loadingText }}</text>
				<text class="loading-hint" v-if="loadingHint">{{ loadingHint }}</text>
				<view v-if="isVideoDownloading && downloadProgress > 0" class="progress-bar">
					<view class="progress-fill" :style="{ width: downloadProgress + '%' }"></view>
				</view>
			</view>
		</view>
		
		<!-- 使用原生video组件播放视频（静音，使用单独的音频） -->
		<video 
			v-if="localVideoPath"
			id="videoPlayer"
			:src="localVideoPath" 
			class="video-player"
			:controls="false"
			:muted="!!audioUrl"
			:show-center-play-btn="false"
			:enable-play-gesture="true"
			:object-fit="'contain'"
			:show-fullscreen-btn="false"
			:show-play-btn="false"
			:show-progress="false"
			:enable-progress-gesture="false"
			:enable-auto-rotation="false"
			:show-mute-btn="false"
			:show-loading="false"
			@error="onVideoError"
			@play="onVideoPlay"
			@pause="onVideoPause"
			@waiting="onVideoWaiting"
			@ended="onVideoEnded"
			@timeupdate="onTimeUpdate"
			@loadedmetadata="onVideoLoadedMetadata"
		></video>

		<!-- 自定义播放按钮：替代系统按钮（避免右下角时间叠层） -->
		<view v-if="localVideoPath && !showLoadingOverlay" class="play-overlay" @click="handleTogglePlay">
			<view v-if="shouldShowPlayButton" class="play-button"></view>
		</view>
	</view>
</template>

<script>
import { API_BASE } from '@/config/api.js';
export default {
	computed: {
		showLoadingOverlay() {
			// 有错误时不显示加载遮罩
			if (this.hasVideoError) return false;
			// 视频已结束不显示加载遮罩
			if (this.videoEnded || this.pausedByAudioEnd) return false;
			if (!this.localVideoPath) return true;
			// 兜底：只要已判定在播放，就不要显示加载遮罩（部分端 play 事件可能延迟/丢失）
			if (this.isVideoPlaying) return false;
			// 等待用户点击播放（小程序禁止自动播放）：不要显示“加载中”遮罩，避免挡住播放入口
			if (this.userPaused && !this.videoHasStarted && !this.isVideoDownloading && !this.videoWaiting) return false;
			if (this.isVideoDownloading && !this.videoHasStarted) return true;
			if (!this.videoHasStarted) return true;
			// 关键修复：如果有 timeupdate（lastVideoTime > 0），说明视频已经在播放，优先判断
			if (this.lastVideoTime > 0) return false;
			if (this.videoWaiting) return true;
			return false;
		},
		shouldShowPlayButton() {
			// 首次未开始播放：始终显示（即使处于 waiting），保证用户有入口点按播放
			if (!this.videoHasStarted) return true;
			// 循环重播/缓冲等自动状态下不显示按钮（仅在已开始播放后生效）
			if (this.isLoopingRestart || this.videoWaiting) return false;
			// 播放中不显示
			if (this.isVideoPlaying) return false;
			// 用户主动暂停：显示
			return !!this.userPaused;
		},
		loadingText() {
			if (this.isVideoDownloading) {
				return this.downloadProgress > 0 ? `下载中 ${this.downloadProgress}%` : '准备下载...';
			}
			if (this.videoWaiting) {
				return '缓冲中...';
			}
			return '加载中...';
		},
		loadingHint() {
			if (this.waitingNeedsUserAction) {
				return '点击屏幕重试';
			}
			if (this.isVideoDownloading && this.downloadProgress > 0) {
				return '首次播放需要下载，请稍候';
			}
			return '';
		},
		videoError() {
			return this.hasVideoError;
		},
		errorMessage() {
			return this.videoErrorMessage || '请检查网络连接后重试';
		}
	},
	data() {
		return {
			API_BASE,
			videoId: '',
			videoUrl: '',
			audioUrl: '',
			videoTitle: '视频播放',
			audioInVideo: false,
			hasAudio: false,
			audioContext: null,
			videoContext: null,
			audioDuration: 0,
			videoDuration: 0,
			isAudioPlaying: false,
			videoLoopTimer: null,
			audioStarted: false,
			audioReady: false,
			audioEnded: false,
			pendingAudioStart: false,
			pendingVideoPlay: false,
			audioStartDelaySec: 1,
			lastVideoTime: 0,
			enableSilencePause: false,
			silenceSegments: [],
			silenceLoaded: false,
			pausedBySilence: false,
			lastAudioTime: 0,
			isLoopingRestart: false,
			userPaused: false,
			pausedByAudioEnd: false,
			videoEndedWaitingAudio: false,
			videoWaiting: false,
			videoEnded: false,
			lastVideoTimeUpdateAt: 0,
			playbackWatchdogTimer: null,
			lastWaitingRecoverAt: 0,
			localVideoPath: '',
			isVideoDownloading: false,
			isVideoReady: false,
			downloadProgress: 0,
			downloadTask: null,
			pendingCacheSaveTempPath: '',
			pendingCacheSaveTimer: null,
			waitingTimer: null,
			waitingSince: 0,
			waitingNeedsUserAction: false,
			videoHasStarted: false,
			isVideoPlaying: false,
			appNetworkFallbackTimer: null,
			appFallbackTriggered: false,
			appNetworkFallbackDelayMs: 900,
			hasShownStorageLimitToast: false,
			cacheKey: '',  // 视频缓存key
			audioCacheKey: '',
			audioStartTime: 0,  // 音频开始播放的时间戳
			videoStartTime: 0,   // 视频开始播放的时间戳
			audioPauseTime: 0,   // 音频暂停时的播放位置
			hasVideoError: false,
			videoErrorMessage: '',
			retryCount: 0,
			maxRetryCount: 3,
			isRestarting: false  // 标记是否正在重新播放
		};
	},
	
	onLoad(options) {
		console.log('[VideoPlayer] 接收参数:', options);
		console.log('[VideoPlayer] BUILD_MARK_NO_EXTERNAL_AUDIO=2026-01-08-01');
		const audioInVideoParam = options.audioInVideo === '1' || options.audioInVideo === 'true';
		if (options.id !== undefined && options.id !== null && String(options.id).trim() !== '') {
			this.videoId = String(options.id).trim();
		}
		if (options.videoUrl) {
			this.videoUrl = decodeURIComponent(options.videoUrl);
		} else if (options.url) {
			this.videoUrl = decodeURIComponent(options.url);
		}
		if (options.audioDelay !== undefined && options.audioDelay !== null && String(options.audioDelay).trim() !== '') {
			const n = Number(options.audioDelay);
			if (!Number.isNaN(n) && n >= 0) {
				this.audioStartDelaySec = n;
			}
		}
		if (options.silencePause !== undefined && options.silencePause !== null && String(options.silencePause).trim() !== '') {
			const sp = String(options.silencePause).trim();
			this.enableSilencePause = sp === '1' || sp === 'true';
		}
		this.videoUrl = this.normalizeMediaUrl(this.videoUrl);
		this.videoEnded = false;
		// #ifdef MP-WEIXIN
		// 微信小程序审核要求：禁止多媒体自动播放。进入页面默认不自动播放，需用户点击后再播放。
		this.userPaused = true;
		// #endif
		// 若传入videoId，则优先从数据库读取 edited_video_url 作为播放源
		if (this.videoId) {
			this.loadVideoFromDb(audioInVideoParam, options);
		} else {
			const matchInVideo = !!(this.videoUrl && this.videoUrl.indexOf('/static/videos/revival_') !== -1);
			const audioInVideo = audioInVideoParam || matchInVideo;
			this.audioInVideo = audioInVideo;
			console.log('[VideoPlayer] audioInVideo判定: audioInVideoParam=', audioInVideoParam, 'matchInVideo=', matchInVideo, 'videoUrl=', this.videoUrl);
			this.cacheKey = this.generateCacheKey(this.videoUrl);
			this.loadVideo();
		}
		if (options.title) {
			this.videoTitle = decodeURIComponent(options.title);
		}
		// 禁用外部音频：播放器只播放视频本身，不叠加/不同步/不处理任何外部音频
		this.hasAudio = false;
		this.audioUrl = '';
		if (audioInVideo) {
			// 视频自带音轨：当音频结束时，视频自然结束并停止（无需额外 loop）
			this.hasAudio = true;
			console.log('[VideoPlayer] audioInVideo=1，使用视频自带音轨');
		} else if (options.audioUrl) {
			console.log('[VideoPlayer] 已忽略外部 audioUrl 参数（禁用外部音频）');
		}
		
		// 初始化视频上下文
		this.$nextTick(() => {
			this.videoContext = uni.createVideoContext('videoPlayer', this);
		});
	},
	
	onUnload() {
		this.stopVideoLoop();
		this.stopPlaybackWatchdog();
		if (this.pendingCacheSaveTimer) {
			clearTimeout(this.pendingCacheSaveTimer);
			this.pendingCacheSaveTimer = null;
		}
		if (this.waitingTimer) {
			clearTimeout(this.waitingTimer);
			this.waitingTimer = null;
		}
		try {
			if (this.downloadTask && typeof this.downloadTask.abort === 'function') {
				this.downloadTask.abort();
			}
		} catch (e) {}
		this.downloadTask = null;
		if (this.audioContext) {
			this.audioContext.stop();
			this.audioContext.destroy();
		}
	},
	
	methods: {
		handleLoadingOverlayClick() {
			if (!this.videoWaiting) return;
			if (!this.videoContext) return;
			if (this.userPaused) return;
			if (this.videoEnded) return;
			// 外置音频播放中时，直接对视频 play 可能触发内核 stop 音频；先暂停音频再尝试恢复
			if (!this.audioInVideo && this.hasAudio && this.audioContext && !this.audioEnded) {
				try {
					if (this.isAudioPlaying) {
						this.audioPauseTime = this.audioContext.currentTime;
						this.audioContext.pause();
					}
				} catch (e) {}
			}
			const t = Number(this.lastVideoTime) || 0;
			const seekTo = Math.max(0, t - 0.3);
			console.log('[VideoPlayer] waiting手动重试: seekTo=', seekTo);
			try {
				this.videoContext.seek && this.videoContext.seek(seekTo);
			} catch (e) {}
			setTimeout(() => {
				if (!this.videoContext) return;
				this.videoContext.play && this.videoContext.play();
			}, 120);
		},

		scheduleSaveVideoToCache(tempPath, delayMs) {
			if (!tempPath) return;
			this.pendingCacheSaveTempPath = tempPath;
			if (this.pendingCacheSaveTimer) {
				clearTimeout(this.pendingCacheSaveTimer);
				this.pendingCacheSaveTimer = null;
			}
			const baseDelay = typeof delayMs === 'number' ? delayMs : 1600;
			this.pendingCacheSaveTimer = setTimeout(() => {
				this.pendingCacheSaveTimer = null;
				const p = this.pendingCacheSaveTempPath;
				if (!p) return;
				// 播放/缓冲中尽量不落盘，避免卡顿；稍后再试
				if (this.isVideoPlaying || this.videoWaiting) {
					this.scheduleSaveVideoToCache(p, 2000);
					return;
				}
				this.pendingCacheSaveTempPath = '';
				uni.saveFile({
					tempFilePath: p,
					success: (saveRes) => {
						const savedPath = saveRes.savedFilePath;
						console.log('[VideoPlayer] 视频已缓存:', savedPath);
						uni.setStorageSync(this.cacheKey, savedPath);
						const cacheInfo = uni.getStorageSync('video_cache_info') || {};
						cacheInfo[this.cacheKey] = {
							path: savedPath,
							url: this.videoUrl,
							time: Date.now()
						};
						uni.setStorageSync('video_cache_info', cacheInfo);
					},
					fail: () => {
						console.log('[VideoPlayer] 视频缓存失败，使用临时文件播放');
					}
				});
			}, Math.max(300, baseDelay));
		},

		async loadVideoFromDb(audioInVideoParam, options) {
			try {
				const token = uni.getStorageSync('token') || '';
				const userId = uni.getStorageSync('userId') || '';
				const url = `${this.API_BASE}/api/photo-revival/videos/${encodeURIComponent(this.videoId)}`;
				console.log('[VideoPlayer] 从数据库加载视频详情:', url);
				const res = await new Promise((resolve, reject) => {
					uni.request({
						url,
						method: 'GET',
						header: {
							'X-User-Id': userId,
							'Authorization': token ? `Bearer ${token}` : ''
						},
						success: resolve,
						fail: reject
					});
				});

				const data = res && res.data ? res.data : null;
				const v = data && data.success ? (data.video || {}) : {};
				const dbUrl = v.edited_video_url || v.local_video_path || v.video_url || '';
				if (dbUrl) {
					this.videoUrl = this.normalizeMediaUrl(String(dbUrl));
					console.log('[VideoPlayer] 使用数据库视频URL:', this.videoUrl);
				} else {
					console.log('[VideoPlayer] 数据库未返回视频URL，回退使用参数videoUrl:', this.videoUrl);
				}

				const matchInVideo = !!(this.videoUrl && this.videoUrl.indexOf('/static/videos/revival_') !== -1);
				const audioInVideo = audioInVideoParam || matchInVideo;
				this.audioInVideo = audioInVideo;
				console.log('[VideoPlayer] audioInVideo判定(DB): audioInVideoParam=', audioInVideoParam, 'matchInVideo=', matchInVideo, 'videoUrl=', this.videoUrl);
				this.cacheKey = this.generateCacheKey(this.videoUrl);
				this.loadVideo();
			} catch (e) {
				console.error('[VideoPlayer] 数据库加载视频详情失败，回退使用参数videoUrl:', e);
				const matchInVideo = !!(this.videoUrl && this.videoUrl.indexOf('/static/videos/revival_') !== -1);
				const audioInVideo = audioInVideoParam || matchInVideo;
				this.audioInVideo = audioInVideo;
				this.cacheKey = this.generateCacheKey(this.videoUrl);
				this.loadVideo();
			}
		},

		handleTogglePlay() {
			if (!this.videoContext) {
				this.$nextTick(() => {
					this.videoContext = uni.createVideoContext('videoPlayer', this);
					this.userPaused = false;
					this.videoContext && this.videoContext.play && this.videoContext.play();
				});
				return;
			}
			
			// 视频已结束，从头播放
			if (this.videoEnded || this.pausedByAudioEnd) {
				console.log('[VideoPlayer] 视频已结束，从头播放');
				this.isRestarting = true;  // 标记正在重新播放
				this.videoEnded = false;
				this.audioEnded = false;
				this.pausedByAudioEnd = false;
				this.videoEndedWaitingAudio = false;
				this.videoWaiting = false;
				this.userPaused = false;
				this.videoHasStarted = false;
				this.lastVideoTime = 0;
				this.isVideoPlaying = false;
				this.retryCount = 0;
				this.stopPlaybackWatchdog();
				// 部分端：结束态下仅 seek(0) + play 不会真正重启，需先 stop
				try {
					this.videoContext.stop && this.videoContext.stop();
				} catch (e) {}
				try {
					this.videoContext.seek && this.videoContext.seek(0);
				} catch (e) {}
				setTimeout(() => {
					if (this.videoContext) {
						this.videoContext.play && this.videoContext.play();
					}
					// 延迟清除标志，确保 onVideoEnded 不会干扰
					setTimeout(() => {
						this.isRestarting = false;
					}, 500);
				}, 100);
				// 触觉反馈
				uni.vibrateShort && uni.vibrateShort({ type: 'light' });
				return;
			}
			
			if (this.isVideoPlaying) {
				this.userPaused = true;
				this.videoContext.pause();
				// 触觉反馈
				uni.vibrateShort && uni.vibrateShort({ type: 'light' });
				return;
			}
			this.userPaused = false;
			this.videoContext.play();
			// 触觉反馈
			uni.vibrateShort && uni.vibrateShort({ type: 'light' });
		},

		startPlaybackWatchdog() {
			if (this.playbackWatchdogTimer) return;
			this.lastVideoTimeUpdateAt = Date.now();
			this.playbackWatchdogTimer = setInterval(() => {
				if (!this.videoContext) return;
				if (this.userPaused) return;
				if (this.videoEnded) return;
				if (!this.videoHasStarted) return;
				if (this.audioEnded) return;
				const now = Date.now();
				// 小程序可能出现“静默暂停/卡住但不触发事件”，用 timeupdate 超时检测
				if (this.isVideoPlaying && this.lastVideoTimeUpdateAt && (now - this.lastVideoTimeUpdateAt) > 2500) {
					console.log('[VideoPlayer] 检测到视频疑似卡住(无timeupdate)，尝试恢复播放。lastVideoTime=', this.lastVideoTime);
					this.videoContext.play();
				}
			}, 1500);
		},

		stopPlaybackWatchdog() {
			if (this.playbackWatchdogTimer) {
				clearInterval(this.playbackWatchdogTimer);
				this.playbackWatchdogTimer = null;
			}
		},
		goBack() {
			try {
				const pages = getCurrentPages ? getCurrentPages() : [];
				if (pages && pages.length > 1) {
					uni.navigateBack();
					return;
				}
			} catch (e) {}
			uni.switchTab({
				url: '/pages/history/history'
			});
		},
		
		// 生成缓存key（使用URL的简单hash）
		generateCacheKey(url) {
			let hash = 0;
			for (let i = 0; i < url.length; i++) {
				const char = url.charCodeAt(i);
				hash = ((hash << 5) - hash) + char;
				hash = hash & hash;
			}
			return 'video_' + Math.abs(hash);
		},
		
		generateAudioCacheKey(url) {
			let hash = 0;
			for (let i = 0; i < url.length; i++) {
				const char = url.charCodeAt(i);
				hash = ((hash << 5) - hash) + char;
				hash = hash & hash;
			}
			return 'audio_' + Math.abs(hash);
		},
		
		normalizeMediaUrl(url) {
			if (!url) return url;
			// Relative path
			if (url.startsWith('/')) {
				return `${this.API_BASE}${url}`;
			}
			// Old hardcoded IP http endpoint -> use current API_BASE (https)
			if (url.startsWith('http://115.190.167.176:20002')) {
				return url.replace('http://115.190.167.176:20002', this.API_BASE);
			}
			return url;
		},
		
		toPlayableLocalPath(filePath) {
			if (!filePath) return filePath;
			try {
				// App(5+) 下部分本地路径需要转换成原生可识别的绝对路径，否则 video 可能黑屏
				if (typeof plus !== 'undefined' && plus.io && typeof plus.io.convertLocalFileSystemURL === 'function') {
					return plus.io.convertLocalFileSystemURL(filePath);
				}
			} catch (e) {}
			return filePath;
		},
		
		// 加载视频（优先使用缓存）
		loadVideo() {
			if (!this.videoUrl) return;
			this.videoHasStarted = false;
			this.videoWaiting = false;
			this.videoEnded = false;
			
			// 1. 检查缓存映射
			const cachedPath = uni.getStorageSync(this.cacheKey);
			if (cachedPath) {
				console.log('[VideoPlayer] 找到缓存视频:', cachedPath);
				// 验证缓存文件是否存在
				uni.getSavedFileInfo({
					filePath: cachedPath,
					success: (res) => {
						console.log('[VideoPlayer] 缓存文件有效，直接使用');
						this.localVideoPath = this.toPlayableLocalPath(cachedPath);
						this.isVideoReady = true;
						this.isVideoDownloading = false;
						this.$nextTick(() => {
							if (!this.videoContext) {
								this.videoContext = uni.createVideoContext('videoPlayer', this);
							}
							setTimeout(() => {
								if (!this.videoContext) return;
								if (this.userPaused) return;
								if (this.videoEnded) return;
								if (!this.videoHasStarted) {
									this.videoContext.play && this.videoContext.play();
								}
							}, 80);
						});
					},
					fail: () => {
						console.log('[VideoPlayer] 缓存文件已失效，重新下载');
						uni.removeStorageSync(this.cacheKey);
						// 缓存失效，使用URL播放并后台下载
						this.localVideoPath = this.videoUrl;
						this.downloadAndCacheVideo();
						this.isVideoReady = true;
						this.$nextTick(() => {
							if (!this.videoContext) {
								this.videoContext = uni.createVideoContext('videoPlayer', this);
							}
							setTimeout(() => {
								if (!this.videoContext) return;
								if (this.userPaused) return;
								if (this.videoEnded) return;
								if (!this.videoHasStarted) {
									this.videoContext.play && this.videoContext.play();
								}
							}, 80);
						});
					}
				});
			} else {
				console.log('[VideoPlayer] 无缓存，先在线播放，后台下载缓存');
				// App端：优先尝试网络URL直连秒开；若短时间无法开始播放则自动切换到下载临时文件兜底
				if (typeof plus !== 'undefined') {
					this.tryAppNetworkPlaybackThenFallback();
					return;
				}
				// 非App：先使用URL直接播放，避免等待整段下载
				this.localVideoPath = this.videoUrl;
				this.isVideoReady = true;
				// #ifndef MP-WEIXIN
				this.downloadAndCacheVideo();
				// #endif
				this.$nextTick(() => {
					if (!this.videoContext) {
						this.videoContext = uni.createVideoContext('videoPlayer', this);
					}
					setTimeout(() => {
						if (!this.videoContext) return;
						if (this.userPaused) return;
						if (this.videoEnded) return;
						if (!this.videoHasStarted) {
							this.videoContext.play && this.videoContext.play();
						}
					}, 80);
				});
			}
		},
		
		tryAppNetworkPlaybackThenFallback() {
			this.appFallbackTriggered = false;
			if (this.appNetworkFallbackTimer) {
				clearTimeout(this.appNetworkFallbackTimer);
				this.appNetworkFallbackTimer = null;
			}
			this.localVideoPath = this.videoUrl;
			this.isVideoReady = true;
			this.$nextTick(() => {
				if (!this.videoContext) {
					this.videoContext = uni.createVideoContext('videoPlayer', this);
				}
				setTimeout(() => {
					if (!this.videoContext) return;
					if (this.userPaused) return;
					if (this.videoEnded) return;
					if (!this.videoHasStarted) {
						this.videoContext.play && this.videoContext.play();
					}
				}, 80);
			});
			const delayMs = Number(this.appNetworkFallbackDelayMs) || 900;
			this.appNetworkFallbackTimer = setTimeout(() => {
				if (this.videoHasStarted) return;
				if (this.appFallbackTriggered) return;
				console.log('[VideoPlayer] App网络直连未能快速开始播放，切换为下载兜底');
				this.appFallbackTriggered = true;
				this.downloadForPlaybackAndCache();
			}, Math.max(300, delayMs));
		},
		
		downloadForPlaybackAndCache() {
			console.log('[VideoPlayer] App无缓存：先下载临时文件播放，再写入缓存:', this.videoUrl);
			this.isVideoDownloading = true;
			this.downloadProgress = 0;
			const task = uni.downloadFile({
				url: this.videoUrl,
				success: (res) => {
					if (res.statusCode !== 200) {
						console.error('[VideoPlayer] App临时下载失败，状态码:', res.statusCode);
						this.isVideoDownloading = false;
						this.isVideoReady = true;
						this.localVideoPath = this.videoUrl;
						this.downloadTask = null;
						return;
					}
					const tempPath = res.tempFilePath;
					this.localVideoPath = this.toPlayableLocalPath(tempPath);
					this.isVideoReady = true;
					this.isVideoDownloading = false;
					this.downloadTask = null;
					this.$nextTick(() => {
						if (!this.videoContext) {
							this.videoContext = uni.createVideoContext('videoPlayer', this);
						}
						setTimeout(() => {
							if (!this.videoContext) return;
							if (this.userPaused) return;
							if (this.videoEnded) return;
							if (!this.videoHasStarted) {
								this.videoContext.play && this.videoContext.play();
							}
						}, 80);
					});
					// 避免与播放抢占IO/资源导致卡顿：延迟写入缓存
					this.scheduleSaveVideoToCache(tempPath, 1800);
				},
				fail: (err) => {
					console.error('[VideoPlayer] App临时下载失败:', err);
					this.isVideoDownloading = false;
					this.isVideoReady = true;
					this.localVideoPath = this.videoUrl;
					this.downloadTask = null;
				}
			});
			this.downloadTask = task || null;
			if (this.downloadTask && typeof this.downloadTask.onProgressUpdate === 'function') {
				this.downloadTask.onProgressUpdate((p) => {
					if (p && typeof p.progress === 'number') {
						this.downloadProgress = Math.max(0, Math.min(100, Math.floor(p.progress)));
					}
				});
			}
		},
		
		// 下载并缓存视频
		downloadAndCacheVideo() {
			// #ifdef MP-WEIXIN
			// 微信小程序端存储空间有限，避免 saveFile 永久缓存，统一走URL直连播放
			return;
			// #endif
			console.log('[VideoPlayer] 开始下载视频:', this.videoUrl);
			this.isVideoDownloading = true;
			this.downloadProgress = 0;
			
			const task = uni.downloadFile({
				url: this.videoUrl,
				success: (res) => {
					if (res.statusCode === 200) {
						console.log('[VideoPlayer] 视频下载成功(临时文件)，延迟写入缓存以避免卡顿');
						this.isVideoReady = true;
						this.isVideoDownloading = false;
						this.downloadTask = null;
						// 避免与播放抢占IO/资源导致卡顿：延迟写入缓存
						this.scheduleSaveVideoToCache(res.tempFilePath, 2200);
					} else {
						console.error('[VideoPlayer] 视频下载失败，状态码:', res.statusCode);
						this.isVideoDownloading = false;
						this.downloadTask = null;
						// 保持使用在线播放
						if (!this.localVideoPath) {
							this.localVideoPath = this.videoUrl;
						}
						this.isVideoReady = true;
						this.downloadTask = null;
					}
				},
				fail: (err) => {
					console.error('[VideoPlayer] 视频下载失败:', err);
					// 下载失败，保持URL直接播放
					if (!this.localVideoPath) {
						this.localVideoPath = this.videoUrl;
					}
					this.isVideoReady = true;
					this.isVideoDownloading = false;
					this.downloadTask = null;
					uni.showToast({
						title: '下载失败，使用在线播放',
						icon: 'none'
					});
				}
			});
			this.downloadTask = task || null;
			if (this.downloadTask && typeof this.downloadTask.onProgressUpdate === 'function') {
				this.downloadTask.onProgressUpdate((p) => {
					if (p && typeof p.progress === 'number') {
						this.downloadProgress = Math.max(0, Math.min(100, Math.floor(p.progress)));
					}
				});
			}
		},
		
		// 初始化音频
		initAudio() {
			if (!this.audioUrl) return;
			
			this.audioContext = uni.createInnerAudioContext();
			this.audioContext.src = this.audioUrl;
			this.audioContext.autoplay = false;
			this.audioReady = false;
			this.audioEnded = false;
			this.pausedBySilence = false;
			this.lastAudioTime = 0;
			this.lastAudioTimeUpdateAt = 0;
			this.silenceSegments = [];
			this.silenceLoaded = false;
			
			this.audioCacheKey = this.generateAudioCacheKey(this.audioUrl);
			const cachedAudioPath = uni.getStorageSync(this.audioCacheKey);
			if (cachedAudioPath) {
				uni.getSavedFileInfo({
					filePath: cachedAudioPath,
					success: () => {
						console.log('[VideoPlayer] 找到缓存音频:', cachedAudioPath);
						if (this.audioContext) {
							this.audioContext.src = cachedAudioPath;
						}
					},
					fail: () => {
						uni.removeStorageSync(this.audioCacheKey);
					}
				});
			}
			
			if (!cachedAudioPath) {
				uni.downloadFile({
					url: this.audioUrl,
					timeout: 60000,
					success: (res) => {
						if (res.statusCode === 200 && this.audioContext) {
							uni.saveFile({
								tempFilePath: res.tempFilePath,
								success: (saveRes) => {
									const savedPath = saveRes.savedFilePath;
									console.log('[VideoPlayer] 音频已缓存:', savedPath);
									uni.setStorageSync(this.audioCacheKey, savedPath);
									if (this.audioContext) {
										this.audioContext.src = savedPath;
									}
								},
								fail: () => {
									console.log('[VideoPlayer] 音频缓存失败，使用临时文件播放');
									this.audioContext.src = res.tempFilePath;
								}
							});
						}
					},
					fail: (err) => {
						console.error('[VideoPlayer] 音频下载失败，回退到URL直连:', err);
					}
				});
			}
			
			if (this.enableSilencePause) {
				this.fetchSilenceSegments();
			}
			
			this.audioContext.onCanplay(() => {
				this.audioReady = true;
				const d = Number(this.audioContext.duration);
				if (!Number.isNaN(d) && d > 0) {
					this.audioDuration = d;
				}
				console.log('[VideoPlayer] 音频时长:', this.audioDuration, '秒');
				// 音频 ready 后：如果视频已开始并满足延迟条件，立即启动并对齐
				if (!this.audioStarted && !this.audioEnded && this.videoHasStarted && this.lastVideoTime >= this.audioStartDelaySec) {
					const startAt = Math.max(0, (this.lastVideoTime || 0) - (this.audioStartDelaySec || 0));
					console.log('[VideoPlayer] 音频已就绪且视频已到达延迟点，开始播放音频，seek=', startAt);
					this.startAudioFromVideoTime(startAt);
					return;
				}
			});
			
			// 某些端 onCanplay 触发时 duration 仍为 0，稍后再取一次
			setTimeout(() => {
				if (this.audioContext) {
					const d = Number(this.audioContext.duration);
					if (!Number.isNaN(d) && d > 0) {
						this.audioDuration = d;
					}
					console.log('[VideoPlayer] 音频时长(延迟校准):', this.audioDuration, '秒');
				}
			}, 200);
			
			this.audioContext.onPlay(() => {
				console.log('[VideoPlayer] 音频开始播放');
				this.isAudioPlaying = true;
			});
			
			if (this.audioContext.onTimeUpdate) {
				this.audioContext.onTimeUpdate(() => {
					if (!this.audioContext) return;
					const t = Number(this.audioContext.currentTime) || 0;
					this.lastAudioTime = t;
					this.lastAudioTimeUpdateAt = Date.now();
					if (this.enableSilencePause) {
						this.handleAudioTimeUpdate(t);
					}
				});
			}
			
			this.audioContext.onPause(() => {
				console.log('[VideoPlayer] 音频被暂停，当前位置:', this.audioContext.currentTime, '秒');
				this.isAudioPlaying = false;
			});
			
			this.audioContext.onStop(() => {
				console.log('[VideoPlayer] 音频被停止');
				this.isAudioPlaying = false;
			});
			
			this.audioContext.onWaiting(() => {
				console.log('[VideoPlayer] 音频加载中...');
			});
			
			this.audioContext.onError((err) => {
				console.error('[VideoPlayer] 音频播放失败:', err);
			});
			
			this.audioContext.onEnded(() => {
				console.log('[VideoPlayer] 音频播放结束，总时长:', this.audioContext.duration, '秒，播放到:', this.audioContext.currentTime, '秒');
				this.isAudioPlaying = false;
				this.audioEnded = true;
				this.pausedBySilence = false;
				this.videoEndedWaitingAudio = false;
				this.stopVideoLoop();

				// 兼容小程序：可能出现音频 duration 获取不准/过早 ended。
				// 只在“确认音频确实播到结尾”时才联动停止视频。
				const audioCur = Number(this.audioContext.currentTime) || 0;
				const audioCurStable = Math.max(audioCur, Number(this.lastAudioTime) || 0);
				const audioDur = Number(this.audioDuration) || Number(this.audioContext.duration) || 0;
				// 小程序端偶发：onEnded 触发时 currentTime=0，但 lastAudioTime 接近结尾。
				// 这里放宽判断：接近结尾或已播放比例很高，都认为“确实结束”，联动停止视频。
				const audioEndedForSure = audioDur > 0 && (
					audioCurStable >= (audioDur - 0.6) ||
					(audioCurStable / audioDur) >= 0.95
				);
				const videoNearEnd = this.videoDuration && this.lastVideoTime >= (this.videoDuration - 0.3);

				if (this.videoContext && (audioEndedForSure || videoNearEnd)) {
					console.log('[VideoPlayer] 音频结束，联动停止视频。audioEndedForSure=', audioEndedForSure, 'videoNearEnd=', videoNearEnd);
					// 清理 waiting 状态，避免 pause 后仍被自动恢复逻辑拉起
					this.videoWaiting = false;
					// 视为“用户暂停”，阻断一切自动恢复/自动播放
					this.userPaused = true;
					this.pausedByAudioEnd = true;
					this.stopPlaybackWatchdog();
					this.videoContext.pause();
					// 兜底：小程序端偶发 pause 未生效/被系统回拉，短延迟再 pause 一次
					setTimeout(() => {
						if (!this.videoContext) return;
						if (!this.audioEnded || !this.pausedByAudioEnd) return;
						this.videoContext.pause && this.videoContext.pause();
					}, 180);
					this.isVideoPlaying = false;
				} else {
					console.log('[VideoPlayer] 音频 onEnded 触发但无法确认已到结尾，跳过联动停止视频。audioDur=', audioDur, 'audioCur=', audioCur, 'lastAudioTime=', this.lastAudioTime);
				}
			});
		},
		
		fetchSilenceSegments() {
			if (!this.audioUrl) return;
			const userId = uni.getStorageSync('userId') || '';
			const token = uni.getStorageSync('token') || '';
			if (!token) {
				return;
			}
			uni.request({
				url: `${this.API_BASE}/api/video/silence-detect`,
				method: 'POST',
				timeout: 120000,
				header: {
					'X-User-Id': userId,
					'Authorization': token ? `Bearer ${token}` : '',
					'Content-Type': 'application/json'
				},
				data: {
					audioUrl: this.audioUrl
				},
				success: (res) => {
					const ok = res.statusCode === 200 && (
						res.data?.success === true ||
						res.data?.success === 'true' ||
						res.data?.success === 1 ||
						res.data?.success === '1'
					);
					if (!ok) {
						console.warn('[VideoPlayer] 静音检测失败:', res.data);
						return;
					}
					const segs = Array.isArray(res.data?.segments) ? res.data.segments : [];
					this.silenceSegments = segs
						.map((s) => ({
							start: Number(s.start) || 0,
							end: Number(s.end) || 0
						}))
						.filter((s) => s.end > s.start)
						.sort((a, b) => a.start - b.start);
					this.silenceLoaded = true;
					console.log('[VideoPlayer] 静音区间数量:', this.silenceSegments.length);
				},
				fail: (err) => {
					console.error('[VideoPlayer] 静音检测请求失败:', err);
				}
			});
		},
		
		handleAudioTimeUpdate(audioTime) {
			if (!this.videoContext || !this.hasAudio) return;
			if (!this.enableSilencePause) return;
			if (!this.silenceLoaded || this.silenceSegments.length === 0) return;
			if (this.audioEnded) return;
			
			let inSilence = false;
			for (const seg of this.silenceSegments) {
				if (audioTime >= seg.start && audioTime < seg.end) {
					inSilence = true;
					break;
				}
				if (audioTime < seg.start) break;
			}
			
			if (inSilence) {
				if (!this.pausedBySilence) {
					this.pausedBySilence = true;
					console.log('[VideoPlayer] 进入停顿区间，暂停视频');
					this.videoContext.pause();
				}
				return;
			}
			
			if (this.pausedBySilence) {
				this.pausedBySilence = false;
				console.log('[VideoPlayer] 结束停顿，继续播放视频');
				this.videoContext.play();
			}
		},
		
		startAudioFromVideoTime(currentTime) {
			if (!this.audioContext || !this.hasAudio) return;
			if (this.audioEnded) return;
			
			// 音频还未 ready 时，等待 onCanplay
			if (!this.audioReady) {
				this.pendingAudioStart = true;
				console.log('[VideoPlayer] 音频未就绪，等待 onCanplay 后启动');
				return;
			}
			
			this.pendingAudioStart = false;
			this.audioStarted = true;  // 标记音频已开始（或已请求开始）
			if (this.audioContext.seek) {
				this.audioContext.seek(Math.max(0, currentTime || 0));
			}
			
			// 获取实时音频时长（可能仍为 0，后面会延迟校准）
			const actualDuration = this.audioContext.duration || this.audioDuration;
			console.log('[VideoPlayer] 实际音频时长:', actualDuration, '秒');
			
			// 使用正常速度播放音频
			this.audioContext.playbackRate = 1;
			console.log('[VideoPlayer] 使用正常速度播放音频');
			
			this.audioStartTime = Date.now();
			this.videoStartTime = Date.now() - (currentTime * 1000);
			console.log('[VideoPlayer] 准备播放音频，URL:', this.audioUrl);
			this.audioContext.play();
		},
		
		retryVideo() {
			this.hasVideoError = false;
			this.videoErrorMessage = '';
			this.retryCount++;
			console.log('[VideoPlayer] 用户手动重试，第', this.retryCount, '次');
			
			// 清理缓存，重新加载
			if (this.cacheKey) {
				try {
					uni.removeStorageSync(this.cacheKey);
				} catch (e) {}
			}
			
			this.localVideoPath = '';
			this.isVideoReady = false;
			this.videoHasStarted = false;
			this.videoWaiting = false;
			this.videoEnded = false;
			
			this.$nextTick(() => {
				this.loadVideo();
			});
		},
		
		onVideoError(e) {
			console.error('[VideoPlayer] 视频加载失败:', e);
			const errMsg = e && e.detail && e.detail.errMsg ? String(e.detail.errMsg) : '';
			const errCode = e && e.detail && (e.detail.errCode !== undefined && e.detail.errCode !== null) ? String(e.detail.errCode) : '';
			const src = this.localVideoPath || this.videoUrl || '';
			// 清掉 waiting，避免错误后 waiting 自动恢复与重试逻辑互相打断
			this.videoWaiting = false;
			
			// 视频已结束或音频已结束，不要重试（避免循环）
			if (this.videoEnded || this.pausedByAudioEnd) {
				console.log('[VideoPlayer] 视频已结束，忽略错误事件，不重试');
				return;
			}
			
			// 视频已经播放到接近结尾（最后1秒内），认为是正常结束，不重试
			if (this.videoDuration > 0 && this.lastVideoTime >= (this.videoDuration - 1)) {
				console.log('[VideoPlayer] 视频已播放到结尾，忽略错误事件，不重试');
				return;
			}
			
			// App端自动切换下载兜底
			if (typeof plus !== 'undefined' && !this.appFallbackTriggered && src === this.videoUrl) {
				console.log('[VideoPlayer] App网络直连播放失败，切换为下载兜底');
				this.appFallbackTriggered = true;
				this.downloadForPlaybackAndCache();
				return;
			}
			
			// 自动重试机制（最多3次）
			if (this.retryCount < this.maxRetryCount) {
				this.retryCount++;
				console.log('[VideoPlayer] 自动重试，第', this.retryCount, '次');
				
				setTimeout(() => {
					// 再次检查是否已结束
					if (this.videoEnded || this.pausedByAudioEnd) {
						console.log('[VideoPlayer] 重试前检测到视频已结束，取消重试');
						return;
					}
					if (this.videoDuration > 0 && this.lastVideoTime >= (this.videoDuration - 1)) {
						console.log('[VideoPlayer] 重试前检测到视频已播放到结尾，取消重试');
						return;
					}
					if (this.cacheKey) {
						try {
							uni.removeStorageSync(this.cacheKey);
						} catch (e) {}
					}
					// 重置部分状态，但保留 videoHasStarted 避免加载遮罩重新显示
					// 标记为“重启中”，忽略期间内核可能抛出的 ended 事件，避免状态机互相打架
					this.isRestarting = true;
					this.isVideoPlaying = false;
					this.videoWaiting = false;
					this.localVideoPath = this.videoUrl;
					this.isVideoReady = true;
					this.$nextTick(() => {
						if (!this.videoContext) {
							this.videoContext = uni.createVideoContext('videoPlayer', this);
						}
						setTimeout(() => {
							if (this.videoContext && !this.userPaused && !this.videoEnded) {
								this.videoContext.play && this.videoContext.play();
							}
							// 短延迟后清除标记，避免长期屏蔽 ended
							setTimeout(() => {
								this.isRestarting = false;
							}, 800);
						}, 100);
					});
				}, 1000 * this.retryCount);
				return;
			}
			
			// 超过重试次数，显示错误界面
			this.hasVideoError = true;
			this.videoErrorMessage = errCode ? `错误代码: ${errCode}` : '网络连接失败或视频格式不支持';
		},
		
		onVideoLoadedMetadata(e) {
			if (e && e.detail && e.detail.duration) {
				this.videoDuration = e.detail.duration;
				console.log('[VideoPlayer] 视频时长:', this.videoDuration, '秒');
			}
			this.videoWaiting = false;
		},
		
		onVideoPlay() {
			console.log('[VideoPlayer] 视频开始播放');
			
			// 如果已经显示错误界面，阻止播放
			if (this.hasVideoError) {
				console.log('[VideoPlayer] 视频加载失败，阻止播放');
				try {
					this.videoContext && this.videoContext.pause && this.videoContext.pause();
				} catch (e) {}
				return;
			}
			
			if (this.appNetworkFallbackTimer) {
				clearTimeout(this.appNetworkFallbackTimer);
				this.appNetworkFallbackTimer = null;
			}
			this.waitingNeedsUserAction = false;
			
			// 清除 waiting 定时器
			if (this.waitingTimer) {
				clearTimeout(this.waitingTimer);
				this.waitingTimer = null;
			}
			
			// 只有在真正结束且用户没有主动重新播放时才阻止
			if (this.videoEnded && this.pausedByAudioEnd) {
				console.log('[VideoPlayer] 视频已结束且被音频结束暂停，阻止继续播放');
				try {
					this.videoContext && this.videoContext.pause && this.videoContext.pause();
				} catch (e) {}
				return;
			}
			this.videoHasStarted = true;
			this.isVideoPlaying = true;
			this.videoWaiting = false;
			this.lastVideoTimeUpdateAt = Date.now();
			this.startPlaybackWatchdog();
			// 如果音频已结束并且是由音频结束触发的暂停，则不允许视频被系统/事件回拉播放
			if (this.audioEnded && this.pausedByAudioEnd && this.videoContext) {
				console.log('[VideoPlayer] 音频已结束，阻止视频继续播放');
				this.videoWaiting = false;
				this.userPaused = true;
				this.videoContext.pause();
				this.isVideoPlaying = false;
				return;
			}
			if (!this.hasAudio || !this.audioContext) return;

			// 无延迟模式：视频一开始播放就尽量启动音频（音频未 ready 则等待 onCanplay）
			if (this.audioStartDelaySec === 0 && !this.audioStarted && !this.audioEnded) {
				if (this.audioReady) {
					const startAt = Math.max(0, this.lastVideoTime || 0);
					console.log('[VideoPlayer] 无延迟模式：视频开始播放，启动音频，seek=', startAt);
					this.startAudioFromVideoTime(startAt);
					return;
				}
				this.pendingAudioStart = true;
				return;
			}
			
			// 如果音频还未就绪，先标记，等 onCanplay 且视频到达延迟点再启动
			if (!this.audioReady) {
				this.pendingAudioStart = true;
				console.log('[VideoPlayer] 音频未就绪，等待音频就绪后在指定延迟点播放');
				return;
			}
			
			// 音频已经开始过且被暂停，则恢复播放
			if (this.audioStarted && !this.isAudioPlaying && !this.audioEnded) {
				console.log('[VideoPlayer] 恢复音频播放，当前位置:', this.audioContext.currentTime, '秒');
				this.audioContext.play();
			}
		},
		
		onVideoPause() {
			console.log('[VideoPlayer] 视频暂停');
			
			// 循环重播时会触发 pause 事件，避免误把音频也暂停
			if (this.isLoopingRestart) {
				return;
			}
			// 系统等待/缓冲导致的暂停：不要暂停音频，避免造成“中断后无法恢复”
			if (this.videoWaiting || !this.userPaused) {
				console.log('[VideoPlayer] 非用户暂停(可能缓冲/系统暂停)，不联动暂停音频');
				this.isVideoPlaying = false;
				return;
			}
			// 同步暂停音频
			if (this.audioContext && this.hasAudio && this.isAudioPlaying) {
				console.log('[VideoPlayer] 暂停音频，当前位置:', this.audioContext.currentTime, '秒');
				this.audioPauseTime = this.audioContext.currentTime;
				this.audioContext.pause();
			}
		},

		onVideoWaiting() {
			// 如果视频已经在正常播放（有持续的 timeupdate），忽略偶发的 waiting 事件
			const now = Date.now();
			if (this.isVideoPlaying && this.lastVideoTimeUpdateAt && (now - this.lastVideoTimeUpdateAt) < 500) {
				console.log('[VideoPlayer] 视频正在播放中，忽略偶发 waiting 事件');
				return;
			}
			
			this.videoWaiting = true;
			this.waitingNeedsUserAction = false;
			this.waitingSince = now;
			console.log('[VideoPlayer] 视频缓冲中(waiting)，lastVideoTime=', this.lastVideoTime);
			if (!this.videoContext) return;
			if (this.waitingTimer) {
				clearTimeout(this.waitingTimer);
				this.waitingTimer = null;
			}
			this.waitingTimer = setTimeout(() => {
				this.waitingTimer = null;
				if (!this.videoWaiting) return;
				if (!this.videoContext) return;
				if (this.userPaused) return;
				if (this.videoEnded) return;
				// 外置音频未结束：不自动强拉起，提示用户点击重试（避免频繁 play 导致音频 stop）
				if (!this.audioInVideo && this.hasAudio && !this.audioEnded) {
					this.waitingNeedsUserAction = true;
					return;
				}
				const t = Number(this.lastVideoTime) || 0;
				const seekTo = Math.max(0, t - 0.3);
				console.log('[VideoPlayer] waiting超时自动恢复: seekTo=', seekTo);
				try {
					this.videoContext.seek && this.videoContext.seek(seekTo);
				} catch (e) {}
				setTimeout(() => {
					if (!this.videoContext) return;
					this.videoContext.play && this.videoContext.play();
				}, 120);
			}, 6000);
			if (typeof plus !== 'undefined' && !this.videoHasStarted && !this.appFallbackTriggered && (this.localVideoPath === this.videoUrl)) {
				console.log('[VideoPlayer] App首帧 waiting，立即切换为下载兜底');
				this.appFallbackTriggered = true;
				this.downloadForPlaybackAndCache();
				return;
			}
			if (this.videoEnded) {
				return;
			}
			// 视频已结束但音频未结束：等待音频即可，不做任何自动恢复
			if (this.videoEndedWaitingAudio && this.hasAudio && !this.audioEnded) {
				console.log('[VideoPlayer] 视频已结束且音频未结束：忽略 waiting');
				return;
			}
			// 关键修复：小程序端只要页面同时存在 video+InnerAudioContext，
			// 在 waiting 阶段频繁调用 video.play() 都可能触发内核把音频系统 stop（504/67）。
			// 这里不依赖 isAudioPlaying（该状态可能抖动/更新延迟），而是只要“有音频且未结束”就跳过。
			if (!this.audioInVideo && this.hasAudio && !this.audioEnded) {
				console.log('[VideoPlayer] 存在音频且未结束，跳过 waiting 自动恢复，以避免音频被系统 stop');
				return;
			}
			// waiting 后稍后尝试自动恢复（非用户暂停、非静音段暂停、非音频已结束），并做节流
			const nowRecover = Date.now();
			if (this.lastWaitingRecoverAt && (nowRecover - this.lastWaitingRecoverAt) < 1500) {
				return;
			}
			this.lastWaitingRecoverAt = nowRecover;
			setTimeout(() => {
				if (!this.videoContext) return;
				if (this.userPaused) return;
				if (this.videoEnded) return;
				if (this.pausedBySilence) return;
				if (this.audioEnded) return;
				console.log('[VideoPlayer] waiting后尝试恢复视频播放');
				this.videoContext.play();
			}, 600);
		},
		
		onVideoEnded() {
			console.log('[VideoPlayer] 视频播放结束', 'lastVideoTime=', this.lastVideoTime, 'videoDuration=', this.videoDuration, 'audioEnded=', this.audioEnded, 'isRestarting=', this.isRestarting);
			
			// 正在重新播放，忽略此次 ended 事件
			if (this.isRestarting) {
				console.log('[VideoPlayer] 正在重新播放，忽略 ended 事件');
				return;
			}
			
			// 如果已经显示错误界面或重试次数已达上限，不要自动重播
			if (this.hasVideoError || this.retryCount >= this.maxRetryCount) {
				console.log('[VideoPlayer] 视频加载失败或重试次数已达上限，阻止自动重播');
				this.isVideoPlaying = false;
				this.videoEnded = true;
				this.stopPlaybackWatchdog();
				return;
			}
			
			this.isVideoPlaying = false;
			this.videoEnded = true;
			this.stopPlaybackWatchdog();
			
			// 视频自带音轨：音轨结束即视频结束。此处强制暂停并阻断任何自动恢复逻辑。
			if (this.audioInVideo) {
				this.videoWaiting = false;
				this.userPaused = true;
				this.pausedByAudioEnd = true;
				this.audioEnded = true;
				this.stopPlaybackWatchdog();
				this.stopVideoLoop();
				try {
					if (this.videoContext && this.videoContext.seek && this.videoDuration) {
						this.videoContext.seek(Math.max(0, Number(this.videoDuration) - 0.05));
					}
				} catch (e) {}
				if (this.videoContext && this.videoContext.pause) {
					this.videoContext.pause();
				}
				return;
			}

			// 音频未结束：不要循环/重播视频（微信内核可能因此 stop 音频）。
			// 改为停在末帧等待音频播放完。
			if (this.hasAudio && this.audioContext && !this.audioEnded) {
				this.videoEndedWaitingAudio = true;
				this.videoWaiting = false;
				console.log('[VideoPlayer] 音频未结束：视频停在末帧等待音频播完（不循环不重播）');
				try {
					if (this.videoContext && this.videoContext.seek && this.videoDuration) {
						this.videoContext.seek(Math.max(0, Number(this.videoDuration) - 0.05));
					}
				} catch (e) {}
				this.videoContext && this.videoContext.pause && this.videoContext.pause();
				return;
			}

			// 兜底：小程序端偶发音频真实结束但未触发 onEnded，导致视频一直循环。
			// 在视频结束时，基于 currentTime/lastAudioTime + duration 再推断一次音频是否已到结尾。
			if (this.hasAudio && this.audioContext && !this.audioEnded) {
				const audioCur = Number(this.audioContext.currentTime) || 0;
				const audioCurStable = Math.max(audioCur, Number(this.lastAudioTime) || 0);
				const audioDur = Number(this.audioDuration) || Number(this.audioContext.duration) || 0;
				const audioEndedForSure = audioDur > 0 && (
					audioCurStable >= (audioDur - 0.6) ||
					(audioCurStable / audioDur) >= 0.95
				);
				if (audioEndedForSure) {
					console.log('[VideoPlayer] 检测到音频已到结尾(兜底推断)，停止视频循环并暂停视频。audioCurStable=', audioCurStable, 'audioDur=', audioDur);
					this.audioEnded = true;
					this.stopVideoLoop();
					if (this.videoContext && this.videoContext.pause) {
						this.videoContext.pause();
					}
					return;
				}
			}

			// 小程序端偶发：缓存视频文件截断/解码异常，导致视频播放到很短时间就 ended。
			// 如果音频还在播放且视频明显“提前结束”，清理缓存并回退到URL重新加载。
			const vd = Number(this.videoDuration) || 0;
			const vt = Number(this.lastVideoTime) || 0;
			const endedTooEarly = (!vd && vt > 0 && vt < 3) || (vd > 0 && vt > 0 && vt < Math.max(1.5, vd - 0.8));
			if (!this.audioEnded && endedTooEarly) {
				console.warn('[VideoPlayer] 检测到视频疑似提前结束，尝试清理缓存并重新加载。vt=', vt, 'vd=', vd, 'cacheKey=', this.cacheKey);
				try {
					if (this.cacheKey) {
						uni.removeStorageSync(this.cacheKey);
					}
				} catch (e) {}
				// 使用URL直连播放并后台重新下载缓存
				this.localVideoPath = this.videoUrl;
				this.isVideoReady = true;
				this.isVideoDownloading = false;
				this.downloadAndCacheVideo();
				// 重新开始播放视频（不影响音频继续）
				this.$nextTick(() => {
					if (!this.videoContext) {
						this.videoContext = uni.createVideoContext('videoPlayer', this);
					}
					setTimeout(() => {
						this.videoContext && this.videoContext.play && this.videoContext.play();
					}, 120);
				});
				return;
			}

			// 无音频或音频已结束：这里不再联动 stop 音频（避免误 stop）。
		},
		
		// 视频时间更新
		onTimeUpdate(e) {
			if (!e || !e.detail) return;
			const currentTime = e.detail.currentTime;
			this.lastVideoTime = currentTime;
			this.lastVideoTimeUpdateAt = Date.now();
			// 兜底：timeupdate 能稳定证明视频已开始播放（部分端 play 事件不可靠）
			if (!this.videoHasStarted && (Number(currentTime) || 0) > 0) {
				this.videoHasStarted = true;
			}
			if (!this.userPaused && (Number(currentTime) || 0) > 0) {
				this.isVideoPlaying = true;
			}
			
			// 视频正常播放中，清除所有等待状态
			if (this.videoWaiting) {
				console.log('[VideoPlayer] timeupdate 清除 waiting 状态，currentTime=', currentTime);
				this.videoWaiting = false;
				this.waitingNeedsUserAction = false;
			}
			
			// 清除 waiting 定时器
			if (this.waitingTimer) {
				clearTimeout(this.waitingTimer);
				this.waitingTimer = null;
			}
			
			// 确保播放状态正确
			if (!this.isVideoPlaying && !this.userPaused) {
				this.isVideoPlaying = true;
			}
			
			// 视频先播，达到延迟点后再启动音频（仅触发一次）
			if (this.hasAudio && this.audioContext && !this.audioStarted && !this.audioEnded) {
				if (currentTime >= this.audioStartDelaySec) {
					if (this.audioReady) {
						const startAt = Math.max(0, (currentTime || 0) - (this.audioStartDelaySec || 0));
						console.log('[VideoPlayer] 视频到达延迟点，开始播放音频，seek=', startAt);
						this.startAudioFromVideoTime(startAt);
						return;
					}
					// 音频还没 ready，继续等待 onCanplay
					this.pendingAudioStart = true;
				}
			}
		},
		
		// 启动视频循环（2-6秒）
		startVideoLoop() {
			// 已经在循环中，不重复启动
			if (this.videoLoopTimer) return;
			
			console.log('[VideoPlayer] 视频循环已启动');
		},
		
		// 停止视频循环
		stopVideoLoop() {
			if (this.videoLoopTimer) {
				clearInterval(this.videoLoopTimer);
				this.videoLoopTimer = null;
				console.log('[VideoPlayer] 视频循环已停止');
			}
		}
	}
};
</script>

<style lang="scss" scoped>
.player-container {
	width: 100%;
	height: 100vh;
	background: #000;
	display: flex;
	flex-direction: column;
	position: relative;
}

.loading-overlay {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.8);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 999;
}

.loading-content {
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 20rpx;
}

.loading-spinner {
	width: 80rpx;
	height: 80rpx;
	border: 6rpx solid rgba(255, 255, 255, 0.3);
	border-top-color: #fff;
	border-radius: 50%;
	animation: spin 1s linear infinite;
}

@keyframes spin {
	to { transform: rotate(360deg); }
}

.loading-text {
	color: #fff;
	font-size: 32rpx;
	font-weight: bold;
}

.loading-hint {
	color: rgba(255, 255, 255, 0.7);
	font-size: 24rpx;
	margin-top: 10rpx;
}

.progress-bar {
	width: 400rpx;
	height: 8rpx;
	background: rgba(255, 255, 255, 0.2);
	border-radius: 4rpx;
	overflow: hidden;
	margin-top: 20rpx;
}

.progress-fill {
	height: 100%;
	background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
	border-radius: 4rpx;
	transition: width 0.3s ease;
}

.header {
	background: rgba(0, 0, 0, 0.8);
	padding: 60rpx 40rpx 20rpx;
	padding-top: calc(60rpx + constant(safe-area-inset-top));
	padding-top: calc(60rpx + env(safe-area-inset-top));
	display: flex;
	justify-content: space-between;
	align-items: center;
	color: white;
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	z-index: 100;
}

.back-btn {
	font-size: 32rpx;
	padding: 16rpx;
	cursor: pointer;
}

.title {
	font-size: 32rpx;
	font-weight: bold;
	flex: 1;
	text-align: center;
}

.placeholder {
	width: 80rpx;
}

.video-player {
	width: 100%;
	height: 100vh;
	background: #000;
}

.play-overlay {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 50;
	display: flex;
	align-items: center;
	justify-content: center;
}

.play-button {
	width: 140rpx;
	height: 140rpx;
	border-radius: 999rpx;
	background: rgba(0, 0, 0, 0.45);
	position: relative;
}

.play-button::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 54%;
	transform: translate(-50%, -50%);
	width: 0;
	height: 0;
	border-top: 22rpx solid transparent;
	border-bottom: 22rpx solid transparent;
	border-left: 34rpx solid #fff;
}

.error-overlay {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.95);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 1000;
}

.error-content {
	text-align: center;
	padding: 60rpx;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 24rpx;
}

.error-icon {
	font-size: 120rpx;
	margin-bottom: 20rpx;
}

.error-title {
	color: #fff;
	font-size: 36rpx;
	font-weight: bold;
}

.error-message {
	color: rgba(255, 255, 255, 0.7);
	font-size: 28rpx;
	line-height: 1.6;
}

.error-actions {
	display: flex;
	gap: 24rpx;
	margin-top: 40rpx;
}

.error-btn {
	padding: 24rpx 48rpx;
	border-radius: 12rpx;
	font-size: 28rpx;
	cursor: pointer;
	transition: all 0.3s ease;
}

.retry-btn {
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	color: #fff;
}

.retry-btn:active {
	transform: scale(0.95);
	opacity: 0.8;
}

.back-btn-error {
	background: rgba(255, 255, 255, 0.1);
	color: #fff;
	border: 2rpx solid rgba(255, 255, 255, 0.3);
}

.back-btn-error:active {
	transform: scale(0.95);
	background: rgba(255, 255, 255, 0.15);
}
</style>
