@qiniu/miku-delivery-mp-ks
Version:
Kuaishou Mini Program SDK for Miku Delivery
159 lines (158 loc) • 6.83 kB
JavaScript
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();
}
}
});