@editframe/elements 0.5.0-beta.4 → 0.5.0-beta.5
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/editor/msToTimeCode.mjs +15 -0
- package/dist/editor/util/EncodedAsset/EncodedAsset.mjs +537 -0
- package/dist/editor/util/MP4File.mjs +161 -0
- package/dist/elements/elements/CrossUpdateController.mjs +16 -0
- package/dist/elements/elements/EFAudio.mjs +37 -0
- package/dist/elements/elements/EFCaptions.mjs +172 -0
- package/dist/elements/elements/EFImage.mjs +67 -0
- package/dist/elements/elements/EFMedia.mjs +255 -0
- package/dist/elements/elements/EFSourceMixin.mjs +57 -0
- package/dist/elements/elements/EFTemporal.mjs +186 -0
- package/dist/elements/elements/EFTimegroup.mjs +230 -0
- package/dist/elements/elements/EFTimeline.mjs +12 -0
- package/dist/elements/elements/EFVideo.mjs +123 -0
- package/dist/elements/elements/EFWaveform.mjs +203 -0
- package/dist/elements/elements/FetchMixin.mjs +30 -0
- package/dist/elements/elements/TimegroupController.mjs +20 -0
- package/dist/elements/elements/durationConverter.mjs +8 -0
- package/dist/elements/elements/parseTimeToMs.mjs +13 -0
- package/dist/elements/elements/util.mjs +11 -0
- package/dist/elements/elements.css.mjs +1 -0
- package/dist/elements/elements.mjs +11 -0
- package/dist/elements/gui/EFFilmstrip.mjs +680 -0
- package/dist/elements/gui/EFWorkbench.mjs +234 -0
- package/dist/elements/gui/TWMixin.css.mjs +4 -0
- package/dist/elements/gui/TWMixin.mjs +27 -0
- package/dist/style.css +754 -0
- package/dist/util/awaitMicrotask.mjs +8 -0
- package/dist/util/memoize.mjs +15 -0
- package/package.json +9 -2
- package/dist/editframe-elements.css +0 -1
- package/dist/editframe-elements.mjs +0 -9089
- package/dist/editframe-elements.umd.js +0 -288
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { EFAudio } from "./EFAudio.mjs";
|
|
2
|
+
import { html, LitElement } from "lit";
|
|
3
|
+
import { property, customElement } from "lit/decorators.js";
|
|
4
|
+
import { EFVideo } from "./EFVideo.mjs";
|
|
5
|
+
import { EFTemporal } from "./EFTemporal.mjs";
|
|
6
|
+
import { CrossUpdateController } from "./CrossUpdateController.mjs";
|
|
7
|
+
import { TWMixin } from "../gui/TWMixin.mjs";
|
|
8
|
+
import { Task } from "@lit/task";
|
|
9
|
+
import * as d3 from "d3";
|
|
10
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
14
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
15
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
16
|
+
if (decorator = decorators[i])
|
|
17
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
18
|
+
if (kind && result)
|
|
19
|
+
__defProp(target, key, result);
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.svgRef = createRef();
|
|
26
|
+
this.mode = "bars";
|
|
27
|
+
this.color = "currentColor";
|
|
28
|
+
this.frameTask = new Task(this, {
|
|
29
|
+
args: () => [this.target.audioBufferTask.status],
|
|
30
|
+
task: async () => {
|
|
31
|
+
await this.target.audioBufferTask.taskComplete;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
createRenderRoot() {
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
render() {
|
|
39
|
+
return html` <svg ${ref(this.svgRef)} class="h-full w-full" store></svg> `;
|
|
40
|
+
}
|
|
41
|
+
connectedCallback() {
|
|
42
|
+
super.connectedCallback();
|
|
43
|
+
if (this.target) {
|
|
44
|
+
new CrossUpdateController(this.target, this);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
drawBars(svg, frequencyData) {
|
|
48
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
49
|
+
const waveHeight = svg.clientHeight;
|
|
50
|
+
const baseline = waveHeight / 2;
|
|
51
|
+
const barX = d3.scaleBand().domain(d3.range(frequencyData.length).map(String)).rangeRound([0, waveWidth]).paddingInner(0.5).paddingOuter(0.01);
|
|
52
|
+
const height = d3.scaleLinear().domain([0, 255]).range([0, baseline]);
|
|
53
|
+
const bars = d3.select(svg).selectAll("rect").data(frequencyData);
|
|
54
|
+
bars.enter().append("rect").merge(bars).attr("x", (_, i) => barX(String(i))).attr("y", (value) => baseline - height(value)).attr("width", barX.bandwidth()).attr("height", (value) => height(value) * 2);
|
|
55
|
+
bars.exit().remove();
|
|
56
|
+
}
|
|
57
|
+
drawBricks(svg, frequencyData) {
|
|
58
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
59
|
+
const waveHeight = svg.clientHeight * devicePixelRatio;
|
|
60
|
+
const brickWidth = waveWidth / frequencyData.length;
|
|
61
|
+
const brickHeightFactor = waveHeight / 255;
|
|
62
|
+
const brickPadding = 2;
|
|
63
|
+
d3.select(svg).selectAll("rect.brick").data(frequencyData).join("rect").attr("class", "brick").attr("x", (_d, i) => i * brickWidth).attr("y", (d) => waveHeight - d * brickHeightFactor).attr("width", brickWidth - brickPadding).attr("height", (d) => d * brickHeightFactor);
|
|
64
|
+
}
|
|
65
|
+
drawLine(svg, frequencyData) {
|
|
66
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
67
|
+
const waveHeight = svg.clientHeight * devicePixelRatio;
|
|
68
|
+
const xScale = d3.scaleLinear().domain([0, frequencyData.length - 1]).range([0, waveWidth]);
|
|
69
|
+
const yScale = d3.scaleLinear().domain([0, 255]).range([waveHeight, 0]);
|
|
70
|
+
const lineGenerator = d3.line().x((_d, i) => xScale(i)).y((d) => yScale(d)).curve(d3.curveCardinal);
|
|
71
|
+
d3.select(svg).selectAll("path").data([frequencyData]).join("path").attr("d", lineGenerator).attr("fill", "none");
|
|
72
|
+
}
|
|
73
|
+
drawRoundBars(svg, frequencyData) {
|
|
74
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
75
|
+
const waveHeight = svg.clientHeight;
|
|
76
|
+
const baseline = waveHeight / 2;
|
|
77
|
+
const barX = d3.scaleBand().domain(d3.range(frequencyData.length).map(String)).rangeRound([0, waveWidth]).paddingInner(0.5).paddingOuter(0.01);
|
|
78
|
+
const height = d3.scaleLinear().domain([0, 255]).range([0, baseline]);
|
|
79
|
+
const bars = d3.select(svg).selectAll("rect").data(frequencyData);
|
|
80
|
+
bars.enter().append("rect").merge(bars).attr("x", (_, i) => barX(String(i))).attr("y", (value) => baseline - height(value)).attr("width", barX.bandwidth()).attr("height", (value) => height(value) * 2).attr("fill", "white").attr("rx", barX.bandwidth() * 0.1).attr("ry", barX.bandwidth() * 0.1);
|
|
81
|
+
bars.exit().remove();
|
|
82
|
+
}
|
|
83
|
+
drawEqualizer(svg, frequencyData) {
|
|
84
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
85
|
+
const waveHeight = svg.clientHeight * devicePixelRatio;
|
|
86
|
+
const barWidth = waveWidth / frequencyData.length;
|
|
87
|
+
const barPadding = 1;
|
|
88
|
+
const heightScale = d3.scaleLinear().domain([0, 255]).range([0, waveHeight]);
|
|
89
|
+
d3.select(svg).selectAll("rect.equalizerBar").data(frequencyData).join("rect").attr("class", "equalizerBar").attr("x", (d, i) => i * barWidth + barPadding / 2).attr("y", (d) => waveHeight - heightScale(d)).attr("width", barWidth - barPadding).attr("height", (d) => heightScale(d));
|
|
90
|
+
svg.selectAll("rect.equalizerBar").transition().duration(100).attr("y", (d) => waveHeight - heightScale(d)).attr("height", (d) => heightScale(d));
|
|
91
|
+
}
|
|
92
|
+
drawCurve(svg, frequencyData) {
|
|
93
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
94
|
+
const waveHeight = svg.clientHeight * devicePixelRatio;
|
|
95
|
+
const xScale = d3.scaleLinear().domain([0, frequencyData.length - 1]).range([0, waveWidth]);
|
|
96
|
+
const yScale = d3.scaleLinear().domain([0, 255]).range([waveHeight, 0]);
|
|
97
|
+
const curveGenerator = d3.line().x((_, i) => xScale(i)).y((d) => yScale(d)).curve(d3.curveNatural);
|
|
98
|
+
d3.select(svg).selectAll("path.curve").data([frequencyData]).join("path").attr("class", "curve").attr("d", curveGenerator).attr("fill", "none");
|
|
99
|
+
}
|
|
100
|
+
drawPixel(svg, frequencyData) {
|
|
101
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
102
|
+
const waveHeight = svg.clientHeight;
|
|
103
|
+
const baseline = waveHeight / 2;
|
|
104
|
+
const barX = d3.scaleBand().domain(d3.range(frequencyData.length).map(String)).rangeRound([0, waveWidth]).paddingInner(0.01).paddingOuter(0.01);
|
|
105
|
+
const height = d3.scaleLinear().domain([0, 255]).range([0, baseline]);
|
|
106
|
+
const bars = d3.select(svg).selectAll("rect").data(frequencyData);
|
|
107
|
+
bars.enter().append("rect").merge(bars).attr("x", (_, i) => barX(String(i))).attr("y", (value) => baseline - height(value)).attr("width", barX.bandwidth()).attr("height", (value) => height(value) * 2);
|
|
108
|
+
bars.exit().remove();
|
|
109
|
+
}
|
|
110
|
+
drawWave(svg, frequencyData) {
|
|
111
|
+
const waveWidth = svg.clientWidth * devicePixelRatio;
|
|
112
|
+
const waveHeight = svg.clientHeight;
|
|
113
|
+
const baseline = waveHeight / 2;
|
|
114
|
+
const barX = d3.scaleBand().domain(d3.range(frequencyData.length).map(String)).rangeRound([0, waveWidth]);
|
|
115
|
+
const height = d3.scaleLinear().domain([0, 255]).range([0, baseline]);
|
|
116
|
+
const bars = d3.select(svg).selectAll("rect").data(frequencyData);
|
|
117
|
+
bars.enter().append("rect").merge(bars).attr("x", (_, i) => barX(String(i))).attr("y", (value) => baseline - height(value)).attr("width", barX.bandwidth()).attr("height", (value) => height(value) * 2);
|
|
118
|
+
bars.exit().remove();
|
|
119
|
+
}
|
|
120
|
+
update(arg) {
|
|
121
|
+
super.update(arg);
|
|
122
|
+
}
|
|
123
|
+
async updated() {
|
|
124
|
+
const svg = this.svgRef.value;
|
|
125
|
+
if (!svg) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!this.target.audioBufferTask.value) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (this.target.ownCurrentTimeMs > 0) {
|
|
132
|
+
const audioContext = new OfflineAudioContext(1, 48e3 / 25, 48e3);
|
|
133
|
+
const audioBufferSource = audioContext.createBufferSource();
|
|
134
|
+
audioBufferSource.buffer = this.target.audioBufferTask.value.buffer;
|
|
135
|
+
const analyser = audioContext.createAnalyser();
|
|
136
|
+
analyser.fftSize = 256;
|
|
137
|
+
audioBufferSource.connect(analyser);
|
|
138
|
+
audioBufferSource.start(
|
|
139
|
+
0,
|
|
140
|
+
Math.max(
|
|
141
|
+
0,
|
|
142
|
+
(this.target.ownCurrentTimeMs - this.target.audioBufferTask.value.startOffsetMs) / 1e3
|
|
143
|
+
),
|
|
144
|
+
48e3 / 1e3
|
|
145
|
+
);
|
|
146
|
+
await audioContext.startRendering();
|
|
147
|
+
const frequencyData = new Uint8Array(analyser.frequencyBinCount);
|
|
148
|
+
analyser.getByteFrequencyData(frequencyData);
|
|
149
|
+
const rect = this.getBoundingClientRect();
|
|
150
|
+
svg.setAttribute("width", (rect.width * devicePixelRatio).toString());
|
|
151
|
+
svg.setAttribute("height", (rect.height * devicePixelRatio).toString());
|
|
152
|
+
switch (this.mode) {
|
|
153
|
+
case "bars":
|
|
154
|
+
this.drawBars(svg, frequencyData);
|
|
155
|
+
break;
|
|
156
|
+
case "bricks":
|
|
157
|
+
this.drawBricks(svg, frequencyData);
|
|
158
|
+
break;
|
|
159
|
+
case "curve":
|
|
160
|
+
this.drawCurve(svg, frequencyData);
|
|
161
|
+
break;
|
|
162
|
+
case "line":
|
|
163
|
+
this.drawLine(svg, frequencyData);
|
|
164
|
+
break;
|
|
165
|
+
case "pixel":
|
|
166
|
+
this.drawPixel(svg, frequencyData);
|
|
167
|
+
break;
|
|
168
|
+
case "wave":
|
|
169
|
+
this.drawWave(svg, frequencyData);
|
|
170
|
+
break;
|
|
171
|
+
case "roundBars":
|
|
172
|
+
this.drawRoundBars(svg, frequencyData);
|
|
173
|
+
break;
|
|
174
|
+
case "equalizer":
|
|
175
|
+
this.drawEqualizer(svg, frequencyData);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
get target() {
|
|
181
|
+
const target = document.querySelector(this.getAttribute("target") ?? "");
|
|
182
|
+
if (target instanceof EFAudio || target instanceof EFVideo) {
|
|
183
|
+
return target;
|
|
184
|
+
}
|
|
185
|
+
throw new Error("Invalid target, must be an EFAudio element");
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
EFWaveform.styles = [];
|
|
189
|
+
__decorateClass([
|
|
190
|
+
property({
|
|
191
|
+
type: String,
|
|
192
|
+
attribute: "mode"
|
|
193
|
+
})
|
|
194
|
+
], EFWaveform.prototype, "mode", 2);
|
|
195
|
+
__decorateClass([
|
|
196
|
+
property({ type: String })
|
|
197
|
+
], EFWaveform.prototype, "color", 2);
|
|
198
|
+
EFWaveform = __decorateClass([
|
|
199
|
+
customElement("ef-waveform")
|
|
200
|
+
], EFWaveform);
|
|
201
|
+
export {
|
|
202
|
+
EFWaveform
|
|
203
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { consume } from "@lit/context";
|
|
2
|
+
import { fetchContext } from "../gui/EFWorkbench.mjs";
|
|
3
|
+
import { state } from "lit/decorators/state.js";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
7
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
8
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
9
|
+
if (decorator = decorators[i])
|
|
10
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
11
|
+
if (kind && result)
|
|
12
|
+
__defProp(target, key, result);
|
|
13
|
+
return result;
|
|
14
|
+
};
|
|
15
|
+
function FetchMixin(superClass) {
|
|
16
|
+
class FetchElement extends superClass {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
this.fetch = fetch.bind(window);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
__decorateClass([
|
|
23
|
+
consume({ context: fetchContext, subscribe: true }),
|
|
24
|
+
state()
|
|
25
|
+
], FetchElement.prototype, "fetch", 2);
|
|
26
|
+
return FetchElement;
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
FetchMixin
|
|
30
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class TimegroupController {
|
|
2
|
+
constructor(host, child) {
|
|
3
|
+
this.host = host;
|
|
4
|
+
this.child = child;
|
|
5
|
+
this.host.addController(this);
|
|
6
|
+
}
|
|
7
|
+
remove() {
|
|
8
|
+
this.host.removeController(this);
|
|
9
|
+
}
|
|
10
|
+
hostDisconnected() {
|
|
11
|
+
this.host.removeController(this);
|
|
12
|
+
}
|
|
13
|
+
hostUpdated() {
|
|
14
|
+
this.child.requestUpdate();
|
|
15
|
+
this.child.currentTimeMs = this.host.currentTimeMs - (this.child.startTimeMs ?? 0);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
TimegroupController
|
|
20
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const parseTimeToMs = (time) => {
|
|
2
|
+
if (time.endsWith("ms")) {
|
|
3
|
+
return parseFloat(time);
|
|
4
|
+
}
|
|
5
|
+
if (time.endsWith("s")) {
|
|
6
|
+
return parseFloat(time) * 1e3;
|
|
7
|
+
} else {
|
|
8
|
+
throw new Error("Time must be in milliseconds or seconds (10s, 10000ms)");
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
parseTimeToMs
|
|
13
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EFTimegroup } from "./EFTimegroup.mjs";
|
|
2
|
+
const getStartTimeMs = (element) => {
|
|
3
|
+
const nearestTimeGroup = element.closest("ef-timegroup");
|
|
4
|
+
if (!(nearestTimeGroup instanceof EFTimegroup)) {
|
|
5
|
+
return 0;
|
|
6
|
+
}
|
|
7
|
+
return nearestTimeGroup.startTimeMs;
|
|
8
|
+
};
|
|
9
|
+
export {
|
|
10
|
+
getStartTimeMs
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "./elements.css.mjs";
|
|
2
|
+
import "./elements/EFTimegroup.mjs";
|
|
3
|
+
import "./elements/EFTimeline.mjs";
|
|
4
|
+
import "./elements/EFImage.mjs";
|
|
5
|
+
import "./elements/EFAudio.mjs";
|
|
6
|
+
import "./elements/EFVideo.mjs";
|
|
7
|
+
import "./elements/EFCaptions.mjs";
|
|
8
|
+
import "./elements/EFWaveform.mjs";
|
|
9
|
+
import "./gui/EFWorkbench.mjs";
|
|
10
|
+
import "./gui/EFFilmstrip.mjs";
|
|
11
|
+
window.EF_REGISTERED = true;
|