@oplayer/hls
Version:
Hls plugin for oplayer
252 lines (251 loc) • 8.89 kB
JavaScript
/**
* 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
};