UNPKG

@qiniu/miku-delivery-mp-ks

Version:

Kuaishou Mini Program SDK for Miku Delivery

159 lines (158 loc) 6.83 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { deferedShared, shouldProxy } from '../common/index'; import { withResolved } from '../dns/common'; import { getDebug } from '../utils/debug'; import { getUuid, MediaError, onAttached, onMediaEvent, resolveSrc } from '../common/component-utils'; const debug = getDebug('components-ks/video'); Component({ properties: { src: String, duration: Number, controls: { type: Boolean, value: true }, autoplay: Boolean, loop: Boolean, muted: Boolean, initialTime: Number, pageGesture: Boolean, direction: Number, showProgress: { type: Boolean, value: true }, showFullscreenBtn: { type: Boolean, value: true }, showPlayBtn: { type: Boolean, value: true }, enableProgressGesture: { type: Boolean, value: true }, objectFit: { type: String, value: 'contain' }, poster: String, showMuteBtn: Boolean, title: String, playBtnPosition: { type: String, value: 'bottom' }, enablePlayGesture: Boolean, autoPauseIfNavigate: { type: Boolean, value: true }, autoPauseIfOpenNative: { type: Boolean, value: true }, vslideGestureInFullscreen: { type: Boolean, value: true }, adUnitId: String, posterForCrawler: String, showCastingButton: Boolean, pictureInPictureShowProgress: Boolean, showScreenLockButton: Boolean, }, data: { // <video> 的 id innerVideoID: '', status: null, finalSrc: '', errorHandler(_event) { } }, lifetimes: { attached() { onAttached(this, 'video'); const id = getUuid(this); this.setData({ // 确保 `innerVideoID` 的值全局唯一,详见 `methods.getVideoContext` innerVideoID: `inner-video-${id}` }); } }, methods: { // 快手小程序文档中号称支持自定义组件 export,详见 https://mp.kuaishou.com/docs/develop/frame/custom_comp/behaviors.html#ks-component-export // 但实测不支持..因此这里还是通过组件 methods `getVideoContext` 来支持外部获取 video context getVideoContext() { if (this.videoContext != null) return this.videoContext; // 我们会确保 `innerVideoID` 的值全局唯一,以避免不同 md-video 互相影响: // `ks.createVideoContext()` 不支持传 `this`,因此如果用相同的 `innerVideoID`,获取到的 video context 会串 return this.videoContext = ks.createVideoContext(this.data.innerVideoID); }, resolveSrc() { var _a; return __awaiter(this, void 0, void 0, function* () { const src = (_a = this.properties.src) !== null && _a !== void 0 ? _a : ''; const { resolver, domains, logger } = yield deferedShared.promise; if (!shouldProxy(src, domains)) { this.setData({ finalSrc: src }); return; } try { yield resolveSrc(this, logger, resolver, src, (resolved) => __awaiter(this, void 0, void 0, function* () { const resolvedSrc = withResolved(src, resolved); const initialized = new Promise((resolve, reject) => { this.setData({ finalSrc: resolvedSrc, errorHandler: (event) => { reject(new MediaError(event.detail.errMsg)); } }); // 快手 `video` 不支持 `loadedmetadata` 事件,也没有其他事件可以去感知“resolvedSrc 是可用的”这件事情; // 因此这里这样决策:如果设置 resolvedSrc 后 2s 内没有触发 `error` 事件,则认为 `resolvedSrc` 是可用的 setTimeout(() => resolve(), 2000); }); return initialized.then(() => { this.setData({ // 如果后续播放再出错,则重新 resolveSrc errorHandler: (event) => { console.warn(`Video error with src (${resolvedSrc}):`, event.detail.errMsg); this.resolveSrc(); // TODO: 这里在 resolveSrc 完成且(metadata)加载成功后,应该尽量去恢复出错前的播放进度 & 状态,否则视频可能会回到初始播放状态 // 这里需要去搞清楚 video 组件的行为细节,才能正确地记录 & 恢复(注意排除用户改变 src 导致的 resolveSrc,这种情况不需要恢复播放状态) // 另外,从 devtool 里观察的行为看,网络请求失败早在 video 抛出 error 事件前就已经发生,考察是否可以尽早捕捉到,而不是等到 video error } }); }); })); } catch (e) { console.warn('video fallback for:', e); this.setData({ finalSrc: src }); } }); }, proxyEvent(event) { if (event.type === 'error') { this.data.errorHandler(event); } this.triggerEvent(event.type, event.detail); onMediaEvent(this, event); } }, observers: { src() { this.resolveSrc(); } } });