@editframe/elements 0.23.8-beta.0 → 0.24.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/elements/EFTimegroup.d.ts +13 -0
- package/dist/elements/EFTimegroup.js +16 -1
- package/dist/gui/PlaybackController.js +5 -1
- package/package.json +2 -2
- package/src/elements/EFCaptions.browsertest.ts +7 -7
- package/src/elements/EFTimegroup.ts +43 -1
- package/src/gui/PlaybackController.ts +6 -1
- package/types.json +1 -1
|
@@ -15,8 +15,21 @@ export declare class EFTimegroup extends EFTimegroup_base {
|
|
|
15
15
|
efContext: this;
|
|
16
16
|
mode: "fit" | "fixed" | "sequence" | "contain";
|
|
17
17
|
overlapMs: number;
|
|
18
|
+
fps: number;
|
|
18
19
|
attributeChangedCallback(name: string, old: string | null, value: string | null): void;
|
|
19
20
|
fit: "none" | "contain" | "cover";
|
|
21
|
+
/**
|
|
22
|
+
* Get the effective FPS for this timegroup.
|
|
23
|
+
* During rendering, uses the render options FPS if available.
|
|
24
|
+
* Otherwise uses the configured fps property.
|
|
25
|
+
*/
|
|
26
|
+
get effectiveFps(): number;
|
|
27
|
+
/**
|
|
28
|
+
* Quantize a time value to the nearest frame boundary based on effectiveFps.
|
|
29
|
+
* @param timeSeconds - Time in seconds
|
|
30
|
+
* @returns Time quantized to frame boundaries in seconds
|
|
31
|
+
*/
|
|
32
|
+
private quantizeToFrameTime;
|
|
20
33
|
private runThrottledFrameTask;
|
|
21
34
|
set currentTime(time: number);
|
|
22
35
|
get currentTime(): number;
|
|
@@ -38,6 +38,7 @@ var EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
|
|
|
38
38
|
this.efContext = this;
|
|
39
39
|
this.mode = "contain";
|
|
40
40
|
this.overlapMs = 0;
|
|
41
|
+
this.fps = 30;
|
|
41
42
|
this.fit = "none";
|
|
42
43
|
this.mediaDurationsPromise = void 0;
|
|
43
44
|
this.frameTask = new Task(this, {
|
|
@@ -88,7 +89,8 @@ var EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
|
|
|
88
89
|
"mode",
|
|
89
90
|
"overlap",
|
|
90
91
|
"currenttime",
|
|
91
|
-
"fit"
|
|
92
|
+
"fit",
|
|
93
|
+
"fps"
|
|
92
94
|
];
|
|
93
95
|
}
|
|
94
96
|
static {
|
|
@@ -112,6 +114,7 @@ var EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
|
|
|
112
114
|
attributeChangedCallback(name, old, value) {
|
|
113
115
|
if (name === "mode" && value) this.mode = value;
|
|
114
116
|
if (name === "overlap" && value) this.overlapMs = parseTimeToMs(value);
|
|
117
|
+
if (name === "fps" && value) this.fps = Number.parseFloat(value);
|
|
115
118
|
super.attributeChangedCallback(name, old, value);
|
|
116
119
|
}
|
|
117
120
|
#resizeObserver;
|
|
@@ -119,11 +122,22 @@ var EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
|
|
|
119
122
|
#seekInProgress = false;
|
|
120
123
|
#pendingSeekTime;
|
|
121
124
|
#processingPendingSeek = false;
|
|
125
|
+
get effectiveFps() {
|
|
126
|
+
if (typeof window !== "undefined" && window.EF_FRAMEGEN?.renderOptions) return window.EF_FRAMEGEN.renderOptions.encoderOptions.video.framerate;
|
|
127
|
+
return this.fps;
|
|
128
|
+
}
|
|
129
|
+
quantizeToFrameTime(timeSeconds) {
|
|
130
|
+
const fps = this.effectiveFps;
|
|
131
|
+
if (!fps || fps <= 0) return timeSeconds;
|
|
132
|
+
const frameDurationS = 1 / fps;
|
|
133
|
+
return Math.round(timeSeconds / frameDurationS) * frameDurationS;
|
|
134
|
+
}
|
|
122
135
|
async runThrottledFrameTask() {
|
|
123
136
|
if (this.playbackController) return this.playbackController.runThrottledFrameTask();
|
|
124
137
|
await this.frameTask.run();
|
|
125
138
|
}
|
|
126
139
|
set currentTime(time) {
|
|
140
|
+
time = this.quantizeToFrameTime(time);
|
|
127
141
|
if (this.playbackController) {
|
|
128
142
|
this.playbackController.currentTime = time;
|
|
129
143
|
return;
|
|
@@ -409,6 +423,7 @@ var EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
|
|
|
409
423
|
};
|
|
410
424
|
__decorate([provide({ context: timegroupContext })], EFTimegroup.prototype, "_timeGroupContext", void 0);
|
|
411
425
|
__decorate([provide({ context: efContext })], EFTimegroup.prototype, "efContext", void 0);
|
|
426
|
+
__decorate([property({ type: Number })], EFTimegroup.prototype, "fps", void 0);
|
|
412
427
|
__decorate([property({ type: String })], EFTimegroup.prototype, "fit", void 0);
|
|
413
428
|
__decorate([property({
|
|
414
429
|
type: Number,
|
|
@@ -68,7 +68,11 @@ var PlaybackController = class {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
get currentTime() {
|
|
71
|
-
|
|
71
|
+
const rawTime = this.#currentTime ?? 0;
|
|
72
|
+
const fps = this.#host.fps ?? 30;
|
|
73
|
+
if (!fps || fps <= 0) return rawTime;
|
|
74
|
+
const frameDurationS = 1 / fps;
|
|
75
|
+
return Math.round(rawTime / frameDurationS) * frameDurationS;
|
|
72
76
|
}
|
|
73
77
|
set currentTime(time) {
|
|
74
78
|
time = Math.max(0, Math.min(this.#host.durationMs / 1e3, time));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1-beta.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"license": "UNLICENSED",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bramus/style-observer": "^1.3.0",
|
|
30
|
-
"@editframe/assets": "0.
|
|
30
|
+
"@editframe/assets": "0.24.1-beta.0",
|
|
31
31
|
"@lit/context": "^1.1.6",
|
|
32
32
|
"@lit/task": "^1.0.3",
|
|
33
33
|
"@opentelemetry/api": "^1.9.0",
|
|
@@ -874,16 +874,16 @@ describe("EFCaptions", () => {
|
|
|
874
874
|
const captionsTask = captions.customCaptionsDataTask;
|
|
875
875
|
await captionsTask.taskComplete;
|
|
876
876
|
|
|
877
|
-
// Test
|
|
878
|
-
timegroup.currentTimeMs =
|
|
877
|
+
// Test at frame before boundary (frame 77 at 30fps = ~2567ms) - should show " timing"
|
|
878
|
+
timegroup.currentTimeMs = 2567;
|
|
879
879
|
await timegroup.seekTask.taskComplete;
|
|
880
880
|
await captions.frameTask.taskComplete;
|
|
881
881
|
await wordContainer.updateComplete;
|
|
882
882
|
|
|
883
|
-
console.log(`At
|
|
883
|
+
console.log(`At 2567ms: wordText="${wordContainer.wordText}"`);
|
|
884
884
|
expect(wordContainer.wordText).toBe(" timing");
|
|
885
885
|
|
|
886
|
-
// Test at exact boundary
|
|
886
|
+
// Test at exact boundary frame (frame 78 at 30fps = 2600ms) - should show " test" (the starting word)
|
|
887
887
|
timegroup.currentTimeMs = 2600;
|
|
888
888
|
await timegroup.seekTask.taskComplete;
|
|
889
889
|
await captions.frameTask.taskComplete;
|
|
@@ -916,14 +916,14 @@ describe("EFCaptions", () => {
|
|
|
916
916
|
const captionsTask = captions.customCaptionsDataTask;
|
|
917
917
|
await captionsTask.taskComplete;
|
|
918
918
|
|
|
919
|
-
// Test
|
|
920
|
-
timegroup.currentTimeMs =
|
|
919
|
+
// Test at frame before boundary (frame 77 at 30fps = ~2567ms)
|
|
920
|
+
timegroup.currentTimeMs = 2567;
|
|
921
921
|
await timegroup.seekTask.taskComplete;
|
|
922
922
|
await captions.frameTask.taskComplete;
|
|
923
923
|
await wordContainer.updateComplete;
|
|
924
924
|
|
|
925
925
|
console.log(
|
|
926
|
-
`Demo case - At
|
|
926
|
+
`Demo case - At 2567ms: wordText="${wordContainer.wordText}", hidden=${wordContainer.hidden}`,
|
|
927
927
|
);
|
|
928
928
|
expect(wordContainer.wordText).toBe(" captions");
|
|
929
929
|
|
|
@@ -60,7 +60,14 @@ export class EFTimegroup extends EFTargetable(EFTemporal(TWMixin(LitElement))) {
|
|
|
60
60
|
static get observedAttributes(): string[] {
|
|
61
61
|
// biome-ignore lint/complexity/noThisInStatic: It's okay to use this here
|
|
62
62
|
const parentAttributes = super.observedAttributes || [];
|
|
63
|
-
return [
|
|
63
|
+
return [
|
|
64
|
+
...parentAttributes,
|
|
65
|
+
"mode",
|
|
66
|
+
"overlap",
|
|
67
|
+
"currenttime",
|
|
68
|
+
"fit",
|
|
69
|
+
"fps",
|
|
70
|
+
];
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
static styles = css`
|
|
@@ -89,6 +96,9 @@ export class EFTimegroup extends EFTargetable(EFTemporal(TWMixin(LitElement))) {
|
|
|
89
96
|
mode: "fit" | "fixed" | "sequence" | "contain" = "contain";
|
|
90
97
|
overlapMs = 0;
|
|
91
98
|
|
|
99
|
+
@property({ type: Number })
|
|
100
|
+
fps = 30;
|
|
101
|
+
|
|
92
102
|
attributeChangedCallback(
|
|
93
103
|
name: string,
|
|
94
104
|
old: string | null,
|
|
@@ -100,6 +110,9 @@ export class EFTimegroup extends EFTargetable(EFTemporal(TWMixin(LitElement))) {
|
|
|
100
110
|
if (name === "overlap" && value) {
|
|
101
111
|
this.overlapMs = parseTimeToMs(value);
|
|
102
112
|
}
|
|
113
|
+
if (name === "fps" && value) {
|
|
114
|
+
this.fps = Number.parseFloat(value);
|
|
115
|
+
}
|
|
103
116
|
super.attributeChangedCallback(name, old, value);
|
|
104
117
|
}
|
|
105
118
|
|
|
@@ -113,6 +126,31 @@ export class EFTimegroup extends EFTargetable(EFTemporal(TWMixin(LitElement))) {
|
|
|
113
126
|
#pendingSeekTime: number | undefined;
|
|
114
127
|
#processingPendingSeek = false;
|
|
115
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Get the effective FPS for this timegroup.
|
|
131
|
+
* During rendering, uses the render options FPS if available.
|
|
132
|
+
* Otherwise uses the configured fps property.
|
|
133
|
+
*/
|
|
134
|
+
get effectiveFps(): number {
|
|
135
|
+
// During rendering, prefer the render options FPS
|
|
136
|
+
if (typeof window !== "undefined" && window.EF_FRAMEGEN?.renderOptions) {
|
|
137
|
+
return window.EF_FRAMEGEN.renderOptions.encoderOptions.video.framerate;
|
|
138
|
+
}
|
|
139
|
+
return this.fps;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Quantize a time value to the nearest frame boundary based on effectiveFps.
|
|
144
|
+
* @param timeSeconds - Time in seconds
|
|
145
|
+
* @returns Time quantized to frame boundaries in seconds
|
|
146
|
+
*/
|
|
147
|
+
private quantizeToFrameTime(timeSeconds: number): number {
|
|
148
|
+
const fps = this.effectiveFps;
|
|
149
|
+
if (!fps || fps <= 0) return timeSeconds;
|
|
150
|
+
const frameDurationS = 1 / fps;
|
|
151
|
+
return Math.round(timeSeconds / frameDurationS) * frameDurationS;
|
|
152
|
+
}
|
|
153
|
+
|
|
116
154
|
private async runThrottledFrameTask(): Promise<void> {
|
|
117
155
|
if (this.playbackController) {
|
|
118
156
|
return this.playbackController.runThrottledFrameTask();
|
|
@@ -122,6 +160,10 @@ export class EFTimegroup extends EFTargetable(EFTemporal(TWMixin(LitElement))) {
|
|
|
122
160
|
|
|
123
161
|
@property({ type: Number, attribute: "currenttime" })
|
|
124
162
|
set currentTime(time: number) {
|
|
163
|
+
// Quantize time to frame boundaries based on fps
|
|
164
|
+
// Do this BEFORE delegating to playbackController to ensure consistency
|
|
165
|
+
time = this.quantizeToFrameTime(time);
|
|
166
|
+
|
|
125
167
|
if (this.playbackController) {
|
|
126
168
|
this.playbackController.currentTime = time;
|
|
127
169
|
return;
|
|
@@ -120,7 +120,12 @@ export class PlaybackController implements ReactiveController {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
get currentTime(): number {
|
|
123
|
-
|
|
123
|
+
const rawTime = this.#currentTime ?? 0;
|
|
124
|
+
// Quantize to frame boundaries based on host's fps
|
|
125
|
+
const fps = (this.#host as any).fps ?? 30;
|
|
126
|
+
if (!fps || fps <= 0) return rawTime;
|
|
127
|
+
const frameDurationS = 1 / fps;
|
|
128
|
+
return Math.round(rawTime / frameDurationS) * frameDurationS;
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
set currentTime(time: number) {
|