@realsee/dnalogel
Version:
871 lines (870 loc) • 37 kB
JavaScript
var D = Object.defineProperty, k = Object.defineProperties;
var H = Object.getOwnPropertyDescriptors;
var S = Object.getOwnPropertySymbols;
var I = Object.prototype.hasOwnProperty, U = Object.prototype.propertyIsEnumerable;
var v = (d, t, e) => t in d ? D(d, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : d[t] = e, E = (d, t) => {
for (var e in t || (t = {}))
I.call(t, e) && v(d, e, t[e]);
if (S)
for (var e of S(t))
U.call(t, e) && v(d, e, t[e]);
return d;
}, x = (d, t) => k(d, H(t));
var P = (d, t, e) => (v(d, typeof t != "symbol" ? t + "" : t, e), e);
var m = (d, t, e) => new Promise((i, s) => {
var o = (a) => {
try {
n(e.next(a));
} catch (l) {
s(l);
}
}, r = (a) => {
try {
n(e.throw(a));
} catch (l) {
s(l);
}
}, n = (a) => a.done ? i(a.value) : Promise.resolve(a.value).then(o, r);
n((e = e.apply(d, t)).next());
});
import { DigitalPlayground as R } from "../core/DigitalPlayground.js";
import { Vector3 as w } from "three";
import { convertMockToPlayer as z } from "../mock.js";
import { transformPosition as N } from "../../shared-utils/five/transformPosition.js";
import "@realsee/five/gltf-loader";
import "../core/DigitalHuman.js";
import "../core/DigitalStateMachine.js";
import "../core/Trace.js";
import "@realsee/five";
const G = "Dnalogel-DigitalPerformancePlugin";
class Q {
constructor(t, e = {}) {
P(this, "five");
P(this, "playground");
P(this, "config");
P(this, "state");
P(this, "NAME");
// 全局进度条相关属性
P(this, "totalDurationCache", null);
P(this, "chapterTimeMapCache", null);
P(this, "progressListeners", /* @__PURE__ */ new Set());
P(this, "progressUpdateInterval", 100);
// 默认100ms更新一次
P(this, "progressUpdateTimer", null);
P(this, "_autoPlayOnNaturalEnd", !1);
// 新增:playing-players-changed 事件监听集合
P(this, "playingPlayersListeners", /* @__PURE__ */ new Set());
this.five = t, this.config = E({
autoPlay: !1,
loop: !1,
debug: !1
}, e), this.playground = new R(this.config.debug), this.state = {
enabled: !0,
playing: !1,
currentChapter: null,
currentScript: null,
continuousPlaying: !1,
playMode: "single"
}, this.NAME = G, this.config.debug, this.init();
}
/**
* 加载剧本数据
*/
loadScript(t) {
return m(this, null, function* () {
try {
if (this.state.currentScript = t, this.playground.setScript(t), this.clearProgressCache(), this.config.debug, yield this.preloadResources(t), this.five && this.five.scene) {
yield new Promise((i) => requestAnimationFrame(i));
let e = 0;
if (t.chapters.forEach((i) => {
i.players.forEach((s) => {
if (s.type === "model") {
const o = this.getDigitalHuman(s.model, s.id);
o != null && o.model && (this.five.scene.add(o.model), e++);
}
});
}), e === 0)
throw new Error("没有模型被添加到场景中");
}
this.config.autoPlay && t.chapters.length > 0 && (yield this.playChapter(t.chapters[0]));
} catch (e) {
throw console.error(`${this.NAME} failed to load script:`, e), e;
}
});
}
init() {
}
/**
* 预加载资源
*/
preloadResources(t) {
return m(this, null, function* () {
const e = [], i = [], s = /* @__PURE__ */ new Set(), o = [], r = /* @__PURE__ */ new Map();
t.chapters.forEach((n) => {
n.players.forEach((a) => {
if (a.type === "model") {
const l = a;
e.push(l.model), i.push(a.id), l.keyframes.forEach((u) => {
u.animation && (s.add(u.animation.url), u.animation.item && (o.push(u.animation.item.url), r.set(u.animation.item.url, {
position: u.animation.item.position,
rotation: u.animation.item.rotation
})));
});
}
});
}), yield Promise.all([
this.playground.loadModels(e, i),
this.playground.loadItems(o, r),
this.playground.loadAnimations(Array.from(s))
]), this.config.debug;
});
}
/**
* 播放指定章节
*/
playChapter(t) {
return m(this, null, function* () {
this.emitPlayingPlayersChanged();
try {
this.state.currentChapter = t, this.state.playing = !0, this.state.playMode = "single", this.config.debug, yield this.playground.playByChapter(t), this.state.playing = !1, this.config.debug;
} catch (e) {
throw console.error(`${this.NAME} failed to play chapter:`, e), this.state.playing = !1, e;
}
});
}
/**
* 播放指定章节(内部方法,用于连续播放)
*/
playChapterForContinuous(t) {
return m(this, null, function* () {
this.emitPlayingPlayersChanged();
try {
this.state.currentChapter = t, this.state.playing = !0, this.config.debug, yield this.playground.playByChapter(t), this.state.continuousPlaying && (this.state.playing = !0), yield new Promise((e) => setTimeout(e, 100)), this.config.debug;
} catch (e) {
throw console.error(`${this.NAME} failed to play chapter for continuous:`, e), this.state.playing = !1, e;
}
});
}
/**
* 播放指定章节(通过索引)
*/
playChapterByIndex(t) {
return m(this, null, function* () {
if (!this.state.currentScript)
throw new Error("No script loaded");
if (t < 0 || t >= this.state.currentScript.chapters.length)
throw new Error("Invalid chapter index");
yield this.playChapter(this.state.currentScript.chapters[t]);
});
}
/**
* 暂停播放
*/
pause() {
this.playground.pause(), this.state.playing = !1, this.state.continuousPlaying = !1, this.config.debug && console.log(`${this.NAME} paused in ${this.state.playMode} mode`);
}
/**
* 继续播放
*/
continue() {
return m(this, null, function* () {
this._autoPlayOnNaturalEnd = !0;
try {
this.config.debug && console.log(`${this.NAME} continuing in ${this.state.playMode} mode`), this.state.playMode === "continuous" ? yield this.continueAllRemaining() : (this.state.currentChapter && this.state.currentChapter.players.forEach((t) => {
if (t.type === "model") {
const e = this.playground.getDigitalHuman(t.model, t.id);
e && e.continueAnimation();
}
}), this.state.playing = !0, this.config.debug && console.log(`${this.NAME} continued single chapter successfully`));
} catch (t) {
throw console.error(`${this.NAME} failed to continue:`, t), this.state.playing = !1, t;
}
});
}
/**
* 连续播放模式下的继续播放(从暂停位置继续播放所有剩余章节)
*/
continueAllRemaining() {
return m(this, null, function* () {
if (!this.state.currentScript || !this.state.currentChapter)
throw new Error("No script or chapter loaded");
const t = this.state.currentScript.chapters, e = t.findIndex((i) => i.name === this.state.currentChapter.name);
if (e === -1)
throw new Error("Current chapter not found in script");
this.state.continuousPlaying = !0;
try {
this.config.debug;
let i = this.getProgress();
console.log(`${this.NAME} [continueAllRemaining] current progress:`, i), this.emitPlayingPlayersChanged();
const s = i && i.currentTime >= 26e3 && i.currentTime <= 27e3, o = i && i.currentTime >= i.totalTime;
console.log(`${this.NAME} [continueAllRemaining] pause analysis:`, {
currentTime: i == null ? void 0 : i.currentTime,
totalTime: i == null ? void 0 : i.totalTime,
isCompleted: o,
isNearHardcodedPause: s
}), o ? (console.log(`${this.NAME} [continueAllRemaining] chapter already completed, restarting`), yield this.playChapterForContinuous(this.state.currentChapter), i = this.getProgress(), console.log(`${this.NAME} [continueAllRemaining] progress after restart:`, i)) : s ? (console.log(`${this.NAME} [continueAllRemaining] detected hardcoded pause, skipping to 27500ms`), this.state.currentChapter.players.filter((n) => n.type === "model").forEach((n) => {
const a = this.playground.getDigitalHuman(n.model, n.id);
a && (a.state.currentTime = 27500);
}), this.state.currentChapter.players.forEach((n) => {
if (n.type === "model") {
const a = this.playground.getDigitalHuman(n.model, n.id);
a && a.continueAnimation();
}
}), this.state.playing = !0, console.log(`${this.NAME} [continueAllRemaining] after hardcoded pause skip - playing:`, this.state.playing)) : (console.log(`${this.NAME} [continueAllRemaining] continuing from paused position`), this.state.currentChapter.players.forEach((r) => {
if (r.type === "model") {
const n = this.playground.getDigitalHuman(r.model, r.id);
n && n.continueAnimation();
}
}), this.state.playing = !0, console.log(`${this.NAME} [continueAllRemaining] after continue - playing:`, this.state.playing)), i = this.getProgress(), console.log(`${this.NAME} [continueAllRemaining] updated progress before wait:`, i, "playing:", this.state.playing), i && i.currentTime < i.totalTime ? yield new Promise((r) => {
let n = (i == null ? void 0 : i.currentTime) || 0, a = 0;
const l = 15, u = () => {
var p;
if (!((p = this.state) != null && p.continuousPlaying)) {
console.log(`${this.NAME} [continueAllRemaining] stopped by user`), r();
return;
}
const h = this.getProgress();
if (this.config.debug, h && h.currentTime >= h.totalTime) {
r();
return;
}
if (h && h.currentTime < 0) {
console.warn(`${this.NAME} [continueAllRemaining] detected negative time, skipping wait`), r();
return;
}
if (h && h.currentTime === n) {
if (a++, a >= l) {
console.warn(`${this.NAME} [continueAllRemaining] progress stagnant for 3s, assuming chapter completed`), r();
return;
}
} else
a = 0, n = (h == null ? void 0 : h.currentTime) || 0;
setTimeout(u, 200);
};
u();
}) : console.log(`${this.NAME} [continueAllRemaining] chapter already completed or restarted, skipping wait`), console.log(
`${this.NAME} [continueAllRemaining] starting to play remaining chapters from index ${e + 1} to ${t.length - 1}`
), this.emitPlayingPlayersChanged();
for (let r = e + 1; r < t.length; r++) {
if (!this.state.continuousPlaying) {
this.config.debug && console.log(`${this.NAME} continuous playback stopped by user at chapter ${r + 1}`);
break;
}
const n = t[r];
this.config.debug;
try {
n.players.filter((h) => h.type === "model").forEach((h) => {
const p = this.playground.getDigitalHuman(h.model, h.id);
p && (p.state.currentTime = 0);
});
const l = Date.now();
yield this.playChapterForContinuous(n);
const u = Date.now() - l;
if (this.config.debug, !this.state.continuousPlaying) {
this.config.debug && console.log(`${this.NAME} continuous playback stopped after chapter ${r + 1}`);
break;
}
r < t.length - 1 && (yield new Promise((h) => setTimeout(h, 1e3)));
} catch (a) {
console.error(`${this.NAME} failed to play chapter ${n.name}:`, a), this.state.continuousPlaying = !1;
break;
}
}
this.config.debug;
} catch (i) {
throw console.error(`${this.NAME} failed during continuous continue:`, i), this.emitPlayingPlayersChanged(), this.state.playing = !1, this.state.continuousPlaying = !1, i;
} finally {
this.state.playing = !1, this.state.continuousPlaying = !1;
}
});
}
/**
* 从当前章节播放到最后一个章节(连续自动播放)
*/
playAllRemaining() {
return m(this, null, function* () {
if (this._autoPlayOnNaturalEnd = !0, !this.state.currentScript)
throw new Error("No script loaded");
const t = this.state.currentScript.chapters;
if (t.length === 0)
throw new Error("No chapters to play");
let e = 0;
this.state.currentChapter && (e = t.findIndex((i) => i.name === this.state.currentChapter.name), e === -1 && (e = 0)), this.state.continuousPlaying = !0, this.state.playMode = "continuous";
try {
this.config.debug;
const i = !this.state.playing && this.state.currentChapter !== null, s = this.getProgress();
if (i) {
const o = s && s.currentTime >= 26e3 && s.currentTime <= 27e3, r = s && s.totalTime > 0 && s.currentTime >= s.totalTime;
if (console.log(`${this.NAME} [playAllRemaining] pause analysis:`, {
hasProgress: !!s,
currentTime: s == null ? void 0 : s.currentTime,
totalTime: s == null ? void 0 : s.totalTime,
isCompleted: r,
isNearHardcodedPause: o,
shouldContinueFromPause: !r || o
}), this.emitPlayingPlayersChanged(), !r || o) {
this.config.debug, yield this.continue();
return;
} else if (this.config.debug, e += 1, e >= t.length) {
this.config.debug, this.state.playing = !1, this.emitPlayingPlayersChanged && this.emitPlayingPlayersChanged();
return;
}
}
for (let o = e; o < t.length; o++) {
if (!this.state.continuousPlaying) {
this.config.debug && console.log(`${this.NAME} continuous playback stopped by user at chapter ${o + 1}`), this.emitPlayingPlayersChanged();
break;
}
const r = t[o];
this.config.debug;
try {
const n = this.state.currentChapter && this.state.currentChapter.name === r.name, a = i && n && o === e;
if (!n || a) {
const h = r.players.filter((p) => p.type === "model");
if (a) {
const p = this.getProgress();
p && p.currentTime >= 26e3 && p.currentTime <= 27e3 ? (h.forEach((b) => {
const M = this.playground.getDigitalHuman(b.model);
M && (M.state.currentTime = 27500);
}), this.config.debug && console.log(`${this.NAME} skipped hardcoded pause for chapter: ${r.name}`)) : this.config.debug && console.log(`${this.NAME} continuing current chapter from existing position: ${r.name}`);
} else
h.forEach((p) => {
const T = this.playground.getDigitalHuman(p.model, p.id);
T && (T.state.currentTime = 0);
}), this.config.debug && console.log(`${this.NAME} reset time for new chapter: ${r.name}`);
} else
this.config.debug;
const l = Date.now();
yield this.playChapterForContinuous(r);
const u = Date.now() - l;
if (this.config.debug, this.state.continuousPlaying && !this.state.playing && (console.warn(`${this.NAME} [playAllRemaining] chapter completed but playing state is false, fixing...`), this.state.playing = !0), !this.state.continuousPlaying) {
this.config.debug && console.log(`${this.NAME} continuous playback stopped after chapter ${o + 1}`), this.emitPlayingPlayersChanged();
break;
}
o < t.length - 1, o < t.length - 1 && (yield new Promise((h) => setTimeout(h, 1e3)));
} catch (n) {
console.error(`${this.NAME} failed to play chapter ${r.name}:`, n), this.emitPlayingPlayersChanged(), this.state.continuousPlaying = !1;
break;
}
}
this.stopAllPlayers(), this.state.playing = !1, this.emitPlayingPlayersChanged && this.emitPlayingPlayersChanged(), this.config.debug;
} catch (i) {
throw console.error(`${this.NAME} failed during continuous playback:`, i), this.emitPlayingPlayersChanged(), this.state.playing = !1, this.state.continuousPlaying = !1, i;
} finally {
this.state.playing = !1, this.state.continuousPlaying = !1;
}
});
}
/**
* 重置播放
*/
reset() {
this.playground.reset(), this.state.playing = !1, this.state.continuousPlaying = !1, this.state.currentChapter = null, this.state.playMode = "single", this.config.debug && console.log(`${this.NAME} reset`);
}
/**
* 设置时间线
*/
setTimelineTo(t) {
this.playground.setTimelineTo(t), this.config.debug && console.log(`${this.NAME} timeline set to:`, t);
}
/**
* 启用插件
*/
enable() {
this.state.enabled = !0, this.config.debug && console.log(`${this.NAME} enabled`);
}
/**
* 禁用插件
*/
disable() {
this.state.enabled = !1, this.pause(), this.config.debug && console.log(`${this.NAME} disabled`);
}
/**
* 重置并清理(完全重置插件状态,包括对 five 的操作和 DigitalHuman)
*/
resetAndCleanup() {
this.stopAllPlayers(), this.playground && (typeof this.playground.dispose == "function" && this.playground.dispose(), this.playground = new this.playground.constructor(this.config.debug || !1)), this.state.playing = !1, this.state.continuousPlaying = !1, this.state.currentChapter = null, this.state.currentScript = null, this.state.enabled = !1, this.state.playMode = "single", this.stopProgressUpdateTimer(), this.clearProgressCache(), this.totalDurationCache = null, this.chapterTimeMapCache = null, this.five && (this.five.models.filter((e) => e.name && (e.name.includes("animation") || e.name.includes("digital"))).forEach((e) => {
this.five.scene.remove(e);
}), this.five.refresh(), this.five.getCurrentState && this.five.getCurrentState()), this._autoPlayOnNaturalEnd = !1, this.config.debug && console.log(`${this.NAME} resetAndCleanup - 完全重置完成(包括 DigitalHuman)`);
}
/**
* 销毁插件(彻底释放资源)
*/
dispose() {
this.resetAndCleanup(), this.playground && typeof this.playground.dispose == "function" && this.playground.dispose(), this.playingPlayersListeners.clear(), this.state = void 0, this.playground = void 0, this.five = void 0, this.config = void 0, typeof window != "undefined" && window.console && console.log(`${this.NAME} disposed (彻底销毁)`);
}
/**
* 获取当前状态
*/
getState() {
return E({}, this.state);
}
/**
* 获取可用章节列表
*/
getChapters() {
var t;
return ((t = this.state.currentScript) == null ? void 0 : t.chapters) || [];
}
/**
* 获取数字人实例
*/
getDigitalHuman(t, e) {
return this.playground.getDigitalHuman(t, e);
}
/**
* 获取当前播放进度信息
*/
getProgress() {
if (!this.state.currentChapter)
return null;
let t = 0, e = 0;
return this.state.currentChapter.players.forEach((i) => {
if (i.type === "model") {
const s = i, o = s.keyframes[s.keyframes.length - 1];
o && (t = Math.max(t, o.timeStamp));
const r = this.playground.getDigitalHuman(s.model, s.id);
r && (e = Math.max(e, r.state.currentTime || 0));
}
}), { currentTime: e, totalTime: t };
}
/**
* 对原始 res 数据进行 transformPosition 处理和章节分组,返回完整结构
* @param res 原始数据
* @param work 可选,存在时才做transformPosition
* @param totalDelay 可选,默认0
*/
transformScriptData(t, e, i = 0) {
var n, a;
if (!t || !t.players || !Array.isArray(t.players))
return t;
const s = (n = t.players[0]) != null && n.fiveState ? E({}, t.players[0].fiveState) : void 0;
if (s && s.offset && e) {
const l = new w(s.offset.x, s.offset.y, s.offset.z), u = N(l, e == null ? void 0 : e.transform);
s.offset = { x: u.x, y: u.y, z: u.z };
}
const o = [
{
id: 0,
name: ((a = t.players[0]) == null ? void 0 : a.chapterName) || "Chapter 1",
players: []
}
];
let r = 0;
return t.players.forEach((l, u) => {
var T, b, M;
let h = E({}, l);
if (e && ((T = h.track) != null && T.path && Array.isArray(h.track.path) && (h.track.path = h.track.path.map((y) => {
const f = N(new w(y.x, y.y, y.z), e == null ? void 0 : e.transform);
return {
x: f.x,
y: f.y,
z: f.z
};
})), (b = h.track) != null && b.points && Array.isArray(h.track.points) && (h.track.points = h.track.points.map((y) => {
if (y.point) {
const f = N(
new w(y.point.x, y.point.y, y.point.z),
e == null ? void 0 : e.transform
);
return x(E({}, y), {
point: {
x: f.x,
y: f.y,
z: f.z
}
});
}
return y;
})), h.matrix && Array.isArray(h.matrix) && h.matrix.length >= 16)) {
const y = new w(h.matrix[12], h.matrix[13], h.matrix[14]), f = N(y, e == null ? void 0 : e.transform);
h.matrix[12] = f.x, h.matrix[13] = f.y, h.matrix[14] = f.z;
}
const p = z(h, i);
((M = l.config) == null ? void 0 : M.type) === "simultaneous" || u > 0 && r++, o[r] || o.push({
id: r,
name: l.chapterName || `Chapter ${r + 1}`,
players: []
}), o[r].players.push(p);
}), {
id: t.id,
name: t.name,
fiveState: s,
chapters: o
};
}
// ===================== 全局进度条功能 =====================
/**
* 清理进度缓存
*/
clearProgressCache() {
this.totalDurationCache = null, this.chapterTimeMapCache = null, this.config.debug && console.log(`${this.NAME} progress cache cleared`);
}
/**
* 计算单个章节的时长
*/
calculateChapterDuration(t) {
let e = 0;
return t.players.forEach((i) => {
if (i.type === "model") {
const s = i, o = s.keyframes[s.keyframes.length - 1];
o && (e = Math.max(e, o.timeStamp));
}
}), e;
}
/**
* 获取所有章节的总时长(毫秒)
*/
getTotalDuration() {
if (this.totalDurationCache !== null)
return this.totalDurationCache;
if (!this.state.currentScript)
return 0;
let t = 0;
return this.state.currentScript.chapters.forEach((e) => {
t += this.calculateChapterDuration(e);
}), this.totalDurationCache = t, this.config.debug && console.log(`${this.NAME} calculated total duration: ${t}ms`), t;
}
/**
* 获取章节时间映射信息
*/
getChapterTimeMap() {
if (this.chapterTimeMapCache !== null)
return this.chapterTimeMapCache;
if (!this.state.currentScript)
return [];
const t = [];
let e = 0;
return this.state.currentScript.chapters.forEach((i, s) => {
const o = this.calculateChapterDuration(i), r = e + o;
t.push({
chapterIndex: s,
chapterName: i.name,
startTime: e,
duration: o,
endTime: r,
chapter: i
}), e = r;
}), this.chapterTimeMapCache = t, this.config.debug && console.log(`${this.NAME} generated chapter time map:`, t), t;
}
/**
* 获取当前全局播放时间(毫秒)
*
* @deprecated 建议使用 getGlobalProgress().currentTime 获取更完整的信息
* @returns 当前全局时间(毫秒)
*/
getCurrentGlobalTime() {
return this.getGlobalProgress().currentTime;
}
/**
* 获取全局进度信息
*/
getGlobalProgress() {
let t = 0, e = 0, i = 0;
if (this.state.currentChapter) {
const n = this.getChapterTimeMap().find((a) => a.chapter.name === this.state.currentChapter.name);
if (n) {
e = n.chapterIndex;
const a = this.getProgress();
i = (a == null ? void 0 : a.currentTime) || 0, t = n.startTime + i;
}
}
const s = this.getTotalDuration(), o = s > 0 ? t / s * 100 : 0;
return {
currentTime: t,
totalTime: s,
percentage: Math.min(100, Math.max(0, o)),
currentChapterIndex: e,
currentChapterTime: i,
currentChapter: this.state.currentChapter || void 0
};
}
/**
* 根据全局时间获取对应的章节信息
*/
getChapterByGlobalTime(t) {
const e = this.getChapterTimeMap();
if (e.length === 0)
return null;
const s = e.find((r) => t >= r.startTime && t < r.endTime) || (t >= e[e.length - 1].startTime ? e[e.length - 1] : null);
if (!s)
return null;
const o = t - s.startTime;
return {
chapterIndex: s.chapterIndex,
chapterTime: Math.max(0, o),
chapter: s.chapter
};
}
/**
* 跳转到指定的全局时间
*/
seekToGlobalTime(t, e) {
return m(this, null, function* () {
var i;
this._autoPlayOnNaturalEnd = !!e;
try {
if (!this.state.currentScript)
throw new Error("No script loaded");
const s = this.getTotalDuration(), o = Math.max(0, Math.min(t, s));
this.config.debug, this.emit("seek-started", o);
const r = this.getChapterByGlobalTime(o);
if (!r)
throw new Error("Unable to locate chapter for the specified time");
const { chapterIndex: n, chapterTime: a, chapter: l } = r, u = this.state.playing;
this.state.playing && this.pause(), (!this.state.currentChapter || this.state.currentChapter.name !== l.name) && (this.config.debug && console.log(`${this.NAME} switching to chapter ${n}: ${l.name}`), yield this.initializeChapterForSeek(l, a), this.state.currentChapter = l, this.emit("chapter-changed", n, l)), this.setTimelineTo(a), a === 0 && this.state.currentScript.chapters.forEach((g) => {
g.players.forEach((C) => {
if (C.type === "model") {
const A = this.playground.getDigitalHuman(C.model, C.id);
A && A.stopAnimation();
}
});
});
const T = this.getChapterTimeMap().find((g) => g.chapter.name === l.name), b = (i = T == null ? void 0 : T.chapterIndex) != null ? i : 0;
this.state.currentScript.chapters.forEach((g, C) => {
g.players.forEach((A) => {
if (A.type === "model") {
const c = this.playground.getDigitalHuman(A.model, A.id);
if (!c)
return;
if (C < b)
c.model.visible = !1, c.container.visible = !1, c.state.currentTime = 0, c.pauseAnimation && c.pauseAnimation();
else if (C === b) {
if (c.play({
keyframes: A.keyframes,
items: A.items,
effects: A.effects
}), c.state.currentTime = a, c.model.visible = !0, c.container.visible = !0, c.state.currentTime = a, c.keyframesManager && c.keyframes) {
const $ = c.keyframesManager.getStateByTime(a, c.keyframes, "model");
$ && (c.model.position.copy($.translation), c.model.quaternion.copy($.quaternion), c.model.scale.copy($.scale), c.model.visible = $.visible);
}
e ? c.continueAnimation() : c.pauseAnimation && c.pauseAnimation();
} else
c.model.visible = !1, c.container.visible = !1, c.state.currentTime = 0, c.pauseAnimation && c.pauseAnimation();
}
});
}), (e !== void 0 ? e : u) && (this.state.playing = !0, l.players.forEach((g) => {
if (g.type === "model") {
const C = this.playground.getDigitalHuman(g.model, g.id);
C && (C.play({
keyframes: g.keyframes,
items: g.items,
effects: g.effects
}), C.state.currentTime = a, C.continueAnimation());
}
}), this.emit("playback-started", o)), this.emit("seek-completed", o);
const f = this.getChapterTimeMap().find((g) => g.chapter.name === l.name);
if (f && Math.abs(a - f.duration) < 1 && // 允许1ms误差
e) {
const g = this.state.currentScript.chapters[n + 1];
g && (this.config.debug && console.log(`${this.NAME} seekToGlobalTime: auto switch to next chapter:`, g.name), yield this.playChapter(g));
}
this.config.debug;
} catch (s) {
throw console.error(`${this.NAME} failed to seek to global time ${t}:`, s), s;
}
});
}
/**
* 初始化章节用于跳转(不自动播放)
*/
initializeChapterForSeek(t, e = 0) {
return m(this, null, function* () {
this.emitPlayingPlayersChanged();
try {
this.config.debug && console.log(`${this.NAME} initializing chapter for seek: ${t.name}`), this.playground.reset();
const i = t.players.filter((r) => r.type === "model"), s = [];
i.forEach((r) => {
const n = r;
n.keyframes && Array.isArray(n.keyframes) && n.keyframes.forEach((a) => {
a.animation && a.animation.url && s.push(a.animation.url);
});
}), s.length > 0 && (yield this.playground.loadAnimations(s)), yield Promise.all(
i.map((r) => m(this, null, function* () {
const n = r;
let a = this.playground.getDigitalHuman(n.model, n.id);
if (a || (yield this.playground.loadModels([n.model], [n.id]), a = this.playground.getDigitalHuman(n.model, n.id)), a && n.keyframes.length > 0) {
a.state.currentTime = e, a.state.currentAnimation = null, a.keyframes = n.keyframes;
const l = n.keyframes[0];
a.model.position.set(l.position[0], l.position[1], l.position[2]), a.model.quaternion.set(
l.quaternion[0],
l.quaternion[1],
l.quaternion[2],
l.quaternion[3]
), a.model.scale.set(l.scale[0], l.scale[1], l.scale[2]), a.model.visible = l.visible, a.play({
keyframes: n.keyframes,
items: n.items,
effects: n.effects
}), a.state.currentTime = e, a.continueAnimation();
}
}))
), t.players.filter((r) => r.type === "camera").length > 0 && this.config.debug, this.config.debug;
} catch (i) {
throw console.error(`${this.NAME} failed to initialize chapter for seek:`, i), i;
}
});
}
/**
* 跳转到指定百分比位置
*/
seekToPercentage(t, e) {
return m(this, null, function* () {
const i = Math.max(0, Math.min(100, t)), o = this.getTotalDuration() * i / 100;
this.config.debug && console.log(`${this.NAME} seeking to ${i}% (${o}ms)`), yield this.seekToGlobalTime(o, e);
});
}
/**
* 跳转到指定章节的指定时间
*/
seekToChapter(t, e = 0, i) {
return m(this, null, function* () {
if (!this.state.currentScript)
throw new Error("No script loaded");
if (t < 0 || t >= this.state.currentScript.chapters.length)
throw new Error("Invalid chapter index");
const r = this.getChapterTimeMap()[t].startTime + Math.max(0, e);
this.config.debug && console.log(`${this.NAME} seeking to chapter ${t} at ${e}ms (global: ${r}ms)`), yield this.seekToGlobalTime(r, i);
});
}
/**
* 从指定全局时间开始播放所有剩余内容
*/
playFromGlobalTime(t) {
return m(this, null, function* () {
yield this.seekToGlobalTime(t, !0), yield this.playAllRemaining();
});
}
/**
* 注册进度变化监听器
*/
onProgressChanged(t) {
const e = (i) => m(this, null, function* () {
var a;
t(i);
const s = ((a = this.state.currentScript) == null ? void 0 : a.chapters) || [], o = this.state.currentChapter ? s.findIndex((l) => l.name === this.state.currentChapter.name) : -1, n = this.getChapterTimeMap().find((l) => {
var u;
return l.chapter.name === ((u = this.state.currentChapter) == null ? void 0 : u.name);
});
if (this.state.playing && this.state.currentChapter && n && i.currentChapterTime >= n.duration - 10 && // 允许10ms误差,比较章节内时间
this._autoPlayOnNaturalEnd) {
const l = s[o + 1];
l ? yield this.playChapter(l) : (this.stopAllPlayers(), this.state.playing = !1, this.five.refresh(), this.emitPlayingPlayersChanged && this.emitPlayingPlayersChanged());
}
});
return this.progressListeners.add(e), this.startProgressUpdateTimer(), this.config.debug && console.log(`${this.NAME} progress listener added, total: ${this.progressListeners.size}`), () => {
this.progressListeners.delete(e), this.progressListeners.size === 0 && this.stopProgressUpdateTimer(), this.config.debug && console.log(`${this.NAME} progress listener removed, remaining: ${this.progressListeners.size}`);
};
}
/**
* 移除进度监听器
*/
offProgressChanged(t) {
this.progressListeners.delete(t), this.progressListeners.size === 0 && this.stopProgressUpdateTimer(), this.config.debug && console.log(`${this.NAME} progress listener removed via off method, remaining: ${this.progressListeners.size}`);
}
/**
* 设置进度回调频率(毫秒)
*/
setProgressUpdateInterval(t) {
const e = this.progressUpdateInterval;
this.progressUpdateInterval = Math.max(50, t), this.config.debug && console.log(`${this.NAME} progress update interval changed from ${e}ms to ${this.progressUpdateInterval}ms`), this.progressUpdateTimer !== null && (this.stopProgressUpdateTimer(), this.startProgressUpdateTimer());
}
/**
* 启动进度更新定时器
*/
startProgressUpdateTimer() {
this.progressUpdateTimer === null && (this.progressUpdateTimer = window.setInterval(() => {
if (this.progressListeners.size > 0) {
const t = this.getGlobalProgress();
this.emit("progress-changed", t);
}
}, this.progressUpdateInterval), this.config.debug && console.log(`${this.NAME} progress update timer started with ${this.progressUpdateInterval}ms interval`));
}
/**
* 停止进度更新定时器
*/
stopProgressUpdateTimer() {
this.progressUpdateTimer !== null && (clearInterval(this.progressUpdateTimer), this.progressUpdateTimer = null, this.config.debug && console.log(`${this.NAME} progress update timer stopped`));
}
/**
* 播放完整的所有章节(从头开始)
*/
playAllChapters() {
return m(this, null, function* () {
if (!this.state.currentScript || this.state.currentScript.chapters.length === 0)
throw new Error("No script or chapters loaded");
this.config.debug, yield this.seekToGlobalTime(0, !0), yield this.playAllRemaining();
});
}
/**
* 获取播放范围信息
*/
getPlaybackRange() {
const t = this.getCurrentGlobalTime(), e = this.getTotalDuration();
return {
startTime: t,
endTime: e,
estimatedDuration: e - t
};
}
/**
* 停止所有 player 的播放状态
*/
stopAllPlayers() {
this.state.currentScript && this.state.currentScript.chapters.forEach((t) => {
t.players.forEach((e) => {
if (e.type === "model") {
const i = this.playground.getDigitalHuman(e.model, e.id);
i && (i.pauseAnimation && i.pauseAnimation(), i.state.playing = !1);
}
});
});
}
// 简单的事件发射器实现
emit(t, ...e) {
if (this.config.debug, t === "progress-changed" && e.length > 0) {
const i = e[0];
this.progressListeners.forEach((s) => {
try {
s(i);
} catch (o) {
console.error(`${this.NAME} progress listener error:`, o);
}
});
}
}
/**
* 注册监听当前正在播放的所有player变化
*/
onPlayingPlayersChanged(t) {
return this.playingPlayersListeners.add(t), () => this.playingPlayersListeners.delete(t);
}
/**
* 获取当前正在播放的所有player(数组)
*/
getCurrentPlayingPlayers() {
const e = this.getGlobalProgress().currentTime, s = this.getChapterTimeMap().find((o) => e >= o.startTime && e < o.endTime);
return s ? this.getPlayingPlayersFromChapter(s.chapter) : [];
}
/**
* 从指定章节获取正在播放的players
*/
getPlayingPlayersFromChapter(t) {
return t.players.filter((e) => {
if (e.type === "model") {
const i = this.playground.getDigitalHuman(e.model, e.id);
return !(!i || !i.state);
}
return !1;
});
}
/**
* 触发 playing-players-changed 事件
*/
emitPlayingPlayersChanged() {
const t = this.getCurrentPlayingPlayers();
this.playingPlayersListeners.forEach((e) => e(t));
}
}
export {
Q as default
};