UNPKG

@oplayer/hls

Version:

Hls plugin for oplayer

252 lines (251 loc) 8.89 kB
/** * name: @oplayer/hls * version: v1.2.28-beta.1 * description: Hls plugin for oplayer * author: shiyiya * homepage: https://github.com/shiyiya/oplayer */ import { loadSDK } from "@oplayer/core"; const PLUGIN_NAME = "oplayer-plugin-hls"; const defaultMatcher = (video, source, forceHLS) => { return source.format === "hls" || source.format === "m3u8" || (source.format === "auto" || typeof source.format === "undefined") && /m3u8(#|\?|$)/i.test(source.src) && (forceHLS || !(Boolean(video.canPlayType("application/x-mpegURL")) || Boolean(video.canPlayType("application/vnd.apple.mpegURL")))); }; class HlsPlugin { constructor(options) { this.key = "hls"; this.name = PLUGIN_NAME; this.version = "1.2.28-beta.1"; this.options = { config: {}, forceHLS: false, textControl: true, audioControl: true, qualityControl: true, withBitrate: false, qualitySwitch: "immediate", matcher: defaultMatcher, defaultQuality: () => -1, defaultAudio: () => -1, defaultSubtitle: () => -1 }; Object.assign(this.options, options); } apply(player) { this.player = player; return this; } async load(_ref, source) { var _a; let $video = _ref.$video; const _this$options = this.options, matcher = _this$options.matcher, forceHLS = _this$options.forceHLS, library = _this$options.library; if (!matcher($video, source, forceHLS)) return false; if (!HlsPlugin.library) { HlsPlugin.library = globalThis.Hls || (library ? await loadSDK(library, "Hls") : (await import("hls.js/dist/hls.min.js")).default); } if (!HlsPlugin.library.isSupported()) return false; const _this$options2 = this.options, config = _this$options2.config, errorHandler = _this$options2.errorHandler; this.instance = new HlsPlugin.library(config); this.instance.attachMedia($video); const instance = this.instance, player = this.player; const $source = document.createElement("source"); $source.setAttribute("src", source.src); $source.setAttribute("type", source.type || (source.type = "application/x-mpegurl")); $source.setAttribute("data-hls", ""); $video.append($source); instance.on(HlsPlugin.library.Events.DESTROYING, () => { $source.remove(); }); instance.on(HlsPlugin.library.Events.ERROR, function(_, data) { if (data.fatal) { switch (data.type) { case "mediaError": instance.recoverMediaError(); break; case "networkError": default: if (errorHandler) { errorHandler(player, data); } else { player.hasError = true; player.emit("error", Object.assign({}, data, { pluginName: PLUGIN_NAME, message: data.type + ": " + (data.reason || data.details) })); } break; } } }); instance.on(HlsPlugin.library.Events.LEVEL_LOADED, (_, data) => { setTimeout(() => { player.emit("canplay", data); }); }); instance.loadSource(source.src); if ((_a = player.context.ui) == null ? void 0 : _a.setting) { generateSetting(player, instance, this.options); } return this; } unload() { var _a; (_a = this.instance) == null ? void 0 : _a.stopLoad(); } destroy() { var _a; if (this.instance) { const player = this.player, instance = this.instance; if ((_a = player.context.ui) == null ? void 0 : _a.setting) removeSetting(player); instance.destroy(); } } } function create(options) { return new HlsPlugin(options); } const generateSetting = (player, instance, options) => { const ui = player.context.ui; if (options.qualityControl) { instance.once(HlsPlugin.library.Events.LEVEL_LOADED, () => { if (instance.levels.length < 2) return; const defaultLevel = options.defaultQuality(instance.levels.toSorted((a, b) => b.bitrate - a.bitrate)); if (defaultLevel != -1) instance.currentLevel = defaultLevel; injectSetting({ icon: ui.icons.quality, name: "Quality", settings() { return instance.levels.toSorted((a, b) => b.bitrate - a.bitrate).reduce((pre, level, id) => { let name = (level.name || level.height).toString(); if (isFinite(+name)) name += "p"; if (options.withBitrate) { const kb = level.bitrate / 1e3; const useMb = kb > 1e3; const number = useMb ? (kb / 1e3).toFixed(2) : Math.floor(kb); name += " (" + number + (useMb ? "m" : "k") + "bps)"; } pre.push({ name, default: defaultLevel == id, value: instance.levels.length - 1 - id }); return pre; }, [{ name: player.locales.get("Auto"), default: instance.autoLevelEnabled, value: -1 }]); }, onChange(it) { if (options.qualitySwitch == "immediate") { instance.currentLevel = it.value; if (it.value !== -1) instance.loadLevel = it.value; } else { instance.nextLevel = it.value; if (it.value !== -1) instance.nextLoadLevel = it.value; } } }); }); instance.on(HlsPlugin.library.Events.LEVEL_SWITCHED, function menuUpdater(_, _ref2) { let level = _ref2.level; if (instance.autoLevelEnabled) { const height = instance.levels[level].height; const levelName = player.locales.get("Auto") + (height ? " (" + height + "p)" : ""); ui.setting.updateLabel(PLUGIN_NAME + "-Quality", levelName); } else { ui.setting.select(PLUGIN_NAME + "-Quality", level - instance.levels.length, false); } }); } if (options.audioControl) { instance.once(HlsPlugin.library.Events.LEVEL_LOADED, () => { var _a; if (instance.audioTracks.length < 2) return; let defaultAudio = options.defaultAudio(instance.audioTracks); if (defaultAudio == -1) { defaultAudio = (_a = instance.audioTracks.find((_ref3) => { let lang = _ref3.lang; return lang === navigator.language || lang === navigator.language.split("-")[0]; })) == null ? void 0 : _a.id; } if (defaultAudio != -1 && defaultAudio != void 0) { instance.audioTrack = defaultAudio; } injectSetting({ icon: ui.icons.lang, name: "Language", settings() { return instance.audioTracks.map((_ref4) => { let name = _ref4.name, lang = _ref4.lang, id = _ref4.id; return { name: lang || name, default: defaultAudio == id, value: id }; }); }, onChange(it) { instance.audioTrack = it.value; } }); }); } if (options.textControl) instance.once(HlsPlugin.library.Events.SUBTITLE_TRACK_LOADED, () => { var _a; if (instance.subtitleTracks.length < 2) return; let defaultSubtitle = options.defaultSubtitle(instance.subtitleTracks); if (defaultSubtitle == -1) { defaultSubtitle = (_a = instance.audioTracks.find((_ref5) => { let lang = _ref5.lang; return lang === navigator.language || lang === navigator.language.split("-")[0]; })) == null ? void 0 : _a.id; } if (defaultSubtitle != -1 && defaultSubtitle != void 0) { instance.subtitleTrack = defaultSubtitle; } injectSetting({ icon: ui.icons.subtitle, name: "Subtitle", settings() { return instance.subtitleTracks.reduce((pre, _ref6) => { let name = _ref6.name, lang = _ref6.lang, id = _ref6.id; pre.push({ name: lang || name, default: defaultSubtitle == id, value: id }); return pre; }, [{ name: player.locales.get("Off"), default: !instance.subtitleDisplay, value: -1 }]); }, onChange(_ref7) { let value = _ref7.value; if (instance.subtitleDisplay = !(value == -1)) { instance.subtitleTrack = value; } } }); }); function injectSetting(arg) { const settings = arg.settings(); const name = arg.name, icon = arg.icon, onChange = arg.onChange; player.context.ui.setting.unregister(PLUGIN_NAME + "-" + name); player.context.ui.setting.register({ name: player.locales.get(name), icon, onChange, type: "selector", key: PLUGIN_NAME + "-" + name, children: settings }); } }; const removeSetting = (player) => { ["Quality", "Language", "Subtitle"].forEach((it) => player.context.ui.setting.unregister(PLUGIN_NAME + "-" + it)); }; export { create as default };