+1
-1
package.json
+1
-1
package.json
+23
-17
pnpm-lock.yaml
+23
-17
pnpm-lock.yaml
···
129
idb:
130
specifier: ^8.0.3
131
version: 8.0.3
132
nanoid:
133
specifier: ^5.1.6
134
version: 5.1.6
···
138
solid-js:
139
specifier: ^1.9.10
140
version: 1.9.10(patch_hash=9cf3f9930aa2f8d4e60502a75153adf9468eb53b42f69e86cac05dfaea3f82e7)
141
-
webm-muxer:
142
-
specifier: ^5.1.4
143
-
version: 5.1.4
144
devDependencies:
145
'@badrap/valita':
146
specifier: ^0.4.6
···
1575
'@types/dom-close-watcher@1.0.0':
1576
resolution: {integrity: sha512-7pL0By56sVVGMSJ3HdSY+u08Id0ljStCaf1VnGFxwfpuNdA0HMz0sl2J24eSi9M6ptl9ySkVK35jF75Fn8trUg==}
1577
1578
'@types/dom-webcodecs@0.1.18':
1579
resolution: {integrity: sha512-vAvE8C9DGWR+tkb19xyjk1TSUlJ7RUzzp4a9Anu7mwBT+fpyePWK1UxmH14tMO5zHmrnrRIMg5NutnnDztLxgg==}
1580
···
1592
1593
'@types/trusted-types@2.0.7':
1594
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
1595
-
1596
-
'@types/wicg-file-system-access@2020.9.8':
1597
-
resolution: {integrity: sha512-ggMz8nOygG7d/stpH40WVaNvBwuyYLnrg5Mbyf6bmsj/8+gb6Ei4ZZ9/4PNpcPNTT8th9Q8sM8wYmWGjMWLX/A==}
1598
1599
acorn-walk@8.3.2:
1600
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
···
2075
2076
magic-string@0.25.9:
2077
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
2078
2079
merge-anything@5.1.7:
2080
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
···
2655
webidl-conversions@4.0.2:
2656
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
2657
2658
-
webm-muxer@5.1.4:
2659
-
resolution: {integrity: sha512-ditzgFVFbfqPaugkIr4mGhAdob5K9HY6Rzlh7TRsA368yA1sp/m5O7nQCcMLdgFDeNGtFPg8B+MeXLtpzKWX6Q==}
2660
-
deprecated: This library is superseded by Mediabunny. Please migrate to it.
2661
-
2662
whatwg-url@7.1.0:
2663
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
2664
···
4114
4115
'@types/dom-close-watcher@1.0.0': {}
4116
4117
'@types/dom-webcodecs@0.1.18': {}
4118
4119
'@types/estree@0.0.39': {}
···
4127
'@types/resolve@1.20.2': {}
4128
4129
'@types/trusted-types@2.0.7': {}
4130
-
4131
-
'@types/wicg-file-system-access@2020.9.8': {}
4132
4133
acorn-walk@8.3.2: {}
4134
···
4575
magic-string@0.25.9:
4576
dependencies:
4577
sourcemap-codec: 1.4.8
4578
4579
merge-anything@5.1.7:
4580
dependencies:
···
5088
vite: 7.2.6(@types/node@24.10.1)(jiti@1.21.7)(terser@5.44.1)
5089
5090
webidl-conversions@4.0.2: {}
5091
-
5092
-
webm-muxer@5.1.4:
5093
-
dependencies:
5094
-
'@types/dom-webcodecs': 0.1.18
5095
-
'@types/wicg-file-system-access': 2020.9.8
5096
5097
whatwg-url@7.1.0:
5098
dependencies:
···
129
idb:
130
specifier: ^8.0.3
131
version: 8.0.3
132
+
mediabunny:
133
+
specifier: ^1.25.7
134
+
version: 1.25.7
135
nanoid:
136
specifier: ^5.1.6
137
version: 5.1.6
···
141
solid-js:
142
specifier: ^1.9.10
143
version: 1.9.10(patch_hash=9cf3f9930aa2f8d4e60502a75153adf9468eb53b42f69e86cac05dfaea3f82e7)
144
devDependencies:
145
'@badrap/valita':
146
specifier: ^0.4.6
···
1575
'@types/dom-close-watcher@1.0.0':
1576
resolution: {integrity: sha512-7pL0By56sVVGMSJ3HdSY+u08Id0ljStCaf1VnGFxwfpuNdA0HMz0sl2J24eSi9M6ptl9ySkVK35jF75Fn8trUg==}
1577
1578
+
'@types/dom-mediacapture-transform@0.1.11':
1579
+
resolution: {integrity: sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==}
1580
+
1581
+
'@types/dom-webcodecs@0.1.13':
1582
+
resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==}
1583
+
1584
'@types/dom-webcodecs@0.1.18':
1585
resolution: {integrity: sha512-vAvE8C9DGWR+tkb19xyjk1TSUlJ7RUzzp4a9Anu7mwBT+fpyePWK1UxmH14tMO5zHmrnrRIMg5NutnnDztLxgg==}
1586
···
1598
1599
'@types/trusted-types@2.0.7':
1600
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
1601
1602
acorn-walk@8.3.2:
1603
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
···
2078
2079
magic-string@0.25.9:
2080
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
2081
+
2082
+
mediabunny@1.25.7:
2083
+
resolution: {integrity: sha512-DL0E1h29HTDaD9bYRXLSSHiAoLbDBksrdYS+4OHWA+aNhQeN+CAGEG7EU6wlhPZ8MOpwXIeC7uv06lo4ziohQQ==}
2084
2085
merge-anything@5.1.7:
2086
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
···
2661
webidl-conversions@4.0.2:
2662
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
2663
2664
whatwg-url@7.1.0:
2665
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
2666
···
4116
4117
'@types/dom-close-watcher@1.0.0': {}
4118
4119
+
'@types/dom-mediacapture-transform@0.1.11':
4120
+
dependencies:
4121
+
'@types/dom-webcodecs': 0.1.18
4122
+
4123
+
'@types/dom-webcodecs@0.1.13': {}
4124
+
4125
'@types/dom-webcodecs@0.1.18': {}
4126
4127
'@types/estree@0.0.39': {}
···
4135
'@types/resolve@1.20.2': {}
4136
4137
'@types/trusted-types@2.0.7': {}
4138
4139
acorn-walk@8.3.2: {}
4140
···
4581
magic-string@0.25.9:
4582
dependencies:
4583
sourcemap-codec: 1.4.8
4584
+
4585
+
mediabunny@1.25.7:
4586
+
dependencies:
4587
+
'@types/dom-mediacapture-transform': 0.1.11
4588
+
'@types/dom-webcodecs': 0.1.13
4589
4590
merge-anything@5.1.7:
4591
dependencies:
···
5099
vite: 7.2.6(@types/node@24.10.1)(jiti@1.21.7)(terser@5.44.1)
5100
5101
webidl-conversions@4.0.2: {}
5102
5103
whatwg-url@7.1.0:
5104
dependencies:
+19
-25
src/components/composer/workers/gif-conversion.ts
+19
-25
src/components/composer/workers/gif-conversion.ts
···
1
import { expose } from 'comlink';
2
-
import { ArrayBufferTarget, Muxer } from 'webm-muxer';
3
4
export type GifWorkerApi = typeof api;
5
const api = {
···
9
10
const frameCount = decoder.tracks.selectedTrack!.frameCount;
11
12
-
let muxer: Muxer<ArrayBufferTarget>;
13
-
let encoder: VideoEncoder | undefined;
14
-
15
if (frameCount === 0) {
16
throw new Error(`GIF has no frames`);
17
}
18
19
-
for (let idx = 0, configured = false; idx < frameCount; idx++) {
20
-
const { image } = await decoder.decode({ frameIndex: idx });
21
-
22
-
if (!configured) {
23
-
const width = image.displayWidth;
24
-
const height = image.displayHeight;
25
26
-
configured = true;
27
28
-
muxer = new Muxer({
29
-
target: new ArrayBufferTarget(),
30
-
video: { codec: 'V_VP9', width, height },
31
-
});
32
33
-
encoder = new VideoEncoder({
34
-
output: (chunk) => muxer.addVideoChunk(chunk),
35
-
error: (err) => console.error(err),
36
-
});
37
38
-
encoder.configure({ codec: 'vp09.00.10.08', width, height });
39
-
}
40
41
-
encoder!.encode(image);
42
}
43
44
-
await encoder!.flush();
45
-
muxer!.finalize();
46
47
-
const buffer = muxer!.target.buffer;
48
return new Blob([buffer], { type: 'video/webm' });
49
},
50
};
···
1
import { expose } from 'comlink';
2
+
import { BufferTarget, Output, VideoSample, VideoSampleSource, WebMOutputFormat } from 'mediabunny';
3
4
export type GifWorkerApi = typeof api;
5
const api = {
···
9
10
const frameCount = decoder.tracks.selectedTrack!.frameCount;
11
12
if (frameCount === 0) {
13
throw new Error(`GIF has no frames`);
14
}
15
16
+
let output: Output<WebMOutputFormat, BufferTarget>;
17
+
let videoSource: VideoSampleSource;
18
19
+
{
20
+
const { image } = await decoder.decode({ frameIndex: 0 });
21
22
+
output = new Output({
23
+
format: new WebMOutputFormat(),
24
+
target: new BufferTarget(),
25
+
});
26
27
+
videoSource = new VideoSampleSource({ codec: 'vp9', bitrate: 1e6 });
28
+
output.addVideoTrack(videoSource);
29
30
+
await output.start();
31
+
await videoSource.add(new VideoSample(image));
32
+
}
33
34
+
for (let idx = 1; idx < frameCount; idx++) {
35
+
const { image } = await decoder.decode({ frameIndex: idx });
36
+
await videoSource.add(new VideoSample(image));
37
}
38
39
+
await output.finalize();
40
41
+
const buffer = output.target.buffer!;
42
return new Blob([buffer], { type: 'video/webm' });
43
},
44
};