import { MiniGamePlatform } from '../platform/MiniGamePlatform'; /** * 激励视频广告(微信 / 抖音 / 快手 / B 站 / OPPO / QQ)。 * * 官方要点: * - 先 onLoad/onError/onClose,再 load/show(抖音) * - 微信/快手:全局单例,show 失败时 load→show(微信文档) * - B 站:show 前 reportScene(1007) * - onClose 仅以 isEnded 判定奖励(快手);微信兼容 res===undefined */ export class RewardedVideoAd { static keys: string[] = []; private static _keyIndex = 0; private static _errCount = 0; private static _callback: Function | null = null; /** 非单例平台当前实例 */ private static _ad: any = null; private static _currentUnitId = ''; /** 微信/快手:adUnitId → 单例实例 */ private static _singletonAds = new Map(); private static _eventsBound = new WeakSet(); /** 防止 onClose 重复触发导致业务回调执行两次 */ private static _sessionFinished = false; static init(keys: string[]) { this.keys = keys ?? []; MiniGamePlatform.init(); if (this.keys.length > 0 && MiniGamePlatform.pf?.createRewardedVideoAd) { const ad = this._ensureAd(this.keys[0]); ad?.load?.(); } } static show(callback?: Function) { this._sessionFinished = false; this._callback = callback ?? null; this._errCount = 0; const pf = MiniGamePlatform.pf; if (!pf?.createRewardedVideoAd) { this._logFail('AD:REWARD_NO_API'); this._finish(false); return; } const adUnitId = this.keys[this._keyIndex]; if (!adUnitId) { this._logFail('AD:REWARD_NO_UNIT'); this._finish(false); return; } const ad = this._ensureAd(adUnitId); if (!ad) { this._logFail('AD:REWARD_CREATE_FAIL', adUnitId); this._finish(false); return; } this._ad = ad; MiniGamePlatform.reportRewardedVideoScene(); this._showWithReload(ad); } /** 激励视频会话是否仍在等待 onClose(用于广告挂起态判断) */ static hasPendingCallback(): boolean { return this._callback != null && !this._sessionFinished; } private static _ensureAd(adUnitId: string): any { const pf = MiniGamePlatform.pf; if (!pf?.createRewardedVideoAd) return null; if (MiniGamePlatform.isRewardedVideoAdSingleton()) { let ad = this._singletonAds.get(adUnitId); if (ad) return ad; ad = pf.createRewardedVideoAd({ adUnitId }); if (!ad) return null; this._bindEvents(ad); this._singletonAds.set(adUnitId, ad); return ad; } if (this._ad && this._currentUnitId === adUnitId) { return this._ad; } if (this._ad) { this._destroyAd(this._ad); this._ad = null; } const ad = pf.createRewardedVideoAd({ adUnitId }); if (!ad) return null; this._currentUnitId = adUnitId; this._bindEvents(ad); ad.load?.(); return ad; } /** 官方推荐:先注册监听,再 load/show */ private static _bindEvents(ad: any): void { if (this._eventsBound.has(ad)) return; this._eventsBound.add(ad); ad.onLoad?.(() => { console.log('[RewardedVideoAd] onLoad'); }); ad.onError?.((err: unknown) => { console.log('[RewardedVideoAd] onError', err); this._logFail('AD:REWARD_ON_ERROR', err); this._onLoadError(); }); ad.onClose?.((res?: { isEnded?: boolean }) => { const ok = MiniGamePlatform.isRewardedVideoCompleted(res); console.log('[RewardedVideoAd] onClose', res, 'completed=', ok); this._finish(ok); ad.load?.(); }); } /** show 失败时 load→show(微信/快手/B站官方示例) */ private static _showWithReload(ad: any): void { let showResult: { then?: Function; catch?: Function } | undefined; try { showResult = ad.show?.(); } catch (err) { console.log('[RewardedVideoAd] show throw', err); this._logFail('AD:REWARD_SHOW_THROW', err); this._onLoadError(); return; } if (!showResult?.then) { return; } showResult.catch((err: unknown) => { console.log('[RewardedVideoAd] show fail, load then show', err); this._logFail('AD:REWARD_SHOW_FAIL', err); const loadResult = ad.load?.(); if (!loadResult?.then) { ad.load?.(); return; } loadResult .then(() => { const retry = ad.show?.(); if (retry?.then) { retry.catch((err2: unknown) => { console.log('[RewardedVideoAd] load+show fail', err2); this._logFail('AD:REWARD_LOAD_SHOW_FAIL', err2); this._onLoadError(); }); } }) .catch((err2: unknown) => { console.log('[RewardedVideoAd] load+show fail', err2); this._logFail('AD:REWARD_LOAD_FAIL', err2); this._onLoadError(); }); }); } private static _onLoadError(): void { this._errCount++; if (this._errCount >= 5 || this.keys.length === 0) { this._finish(false); return; } const oldKey = this.keys[this._keyIndex]; this._keyIndex = (this._keyIndex + 1) % this.keys.length; const newKey = this.keys[this._keyIndex]; if (MiniGamePlatform.isRewardedVideoAdSingleton()) { this._singletonAds.delete(oldKey); } else if (this._ad) { this._destroyAd(this._ad); this._ad = null; this._currentUnitId = ''; } const ad = this._ensureAd(newKey); if (!ad) { this._logFail('AD:REWARD_SWITCH_FAIL', newKey); this._finish(false); return; } this._ad = ad; this._showWithReload(ad); } private static _destroyAd(ad: any): void { if (!ad?.destroy || MiniGamePlatform.isRewardedVideoAdSingleton()) return; try { ad.destroy(); } catch (e) { console.warn('[RewardedVideoAd] destroy failed', e); } this._eventsBound.delete(ad); } private static _finish(success: boolean): void { if (this._sessionFinished) return; this._sessionFinished = true; if (!success) { this._logFail('AD:REWARD_FINISH_FAIL'); } const cb = this._callback; this._callback = null; if (cb) cb(success); } private static _logFail(tag: string, detail?: unknown): void { try { gg.sdk?.pfLogKey?.(tag, detail); } catch { /* ignore */ } } }