playsvideo 0.0.1

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/adapters/node-ffmpeg.d.ts +15 -0
  4. package/dist/adapters/node-ffmpeg.d.ts.map +1 -0
  5. package/dist/adapters/node-ffmpeg.js +35 -0
  6. package/dist/adapters/node-ffmpeg.js.map +1 -0
  7. package/dist/adapters/node-ffprobe.d.ts +12 -0
  8. package/dist/adapters/node-ffprobe.d.ts.map +1 -0
  9. package/dist/adapters/node-ffprobe.js +55 -0
  10. package/dist/adapters/node-ffprobe.js.map +1 -0
  11. package/dist/engine.d.ts +51 -0
  12. package/dist/engine.d.ts.map +1 -0
  13. package/dist/engine.js +386 -0
  14. package/dist/engine.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/pipeline/adts-parse.d.ts +3 -0
  20. package/dist/pipeline/adts-parse.d.ts.map +1 -0
  21. package/dist/pipeline/adts-parse.js +36 -0
  22. package/dist/pipeline/adts-parse.js.map +1 -0
  23. package/dist/pipeline/audio-transcode.d.ts +42 -0
  24. package/dist/pipeline/audio-transcode.d.ts.map +1 -0
  25. package/dist/pipeline/audio-transcode.js +147 -0
  26. package/dist/pipeline/audio-transcode.js.map +1 -0
  27. package/dist/pipeline/codec-probe.d.ts +22 -0
  28. package/dist/pipeline/codec-probe.d.ts.map +1 -0
  29. package/dist/pipeline/codec-probe.js +70 -0
  30. package/dist/pipeline/codec-probe.js.map +1 -0
  31. package/dist/pipeline/demux.d.ts +23 -0
  32. package/dist/pipeline/demux.d.ts.map +1 -0
  33. package/dist/pipeline/demux.js +97 -0
  34. package/dist/pipeline/demux.js.map +1 -0
  35. package/dist/pipeline/mux.d.ts +15 -0
  36. package/dist/pipeline/mux.d.ts.map +1 -0
  37. package/dist/pipeline/mux.js +60 -0
  38. package/dist/pipeline/mux.js.map +1 -0
  39. package/dist/pipeline/pipeline.d.ts +22 -0
  40. package/dist/pipeline/pipeline.d.ts.map +1 -0
  41. package/dist/pipeline/pipeline.js +112 -0
  42. package/dist/pipeline/pipeline.js.map +1 -0
  43. package/dist/pipeline/playlist.d.ts +5 -0
  44. package/dist/pipeline/playlist.d.ts.map +1 -0
  45. package/dist/pipeline/playlist.js +72 -0
  46. package/dist/pipeline/playlist.js.map +1 -0
  47. package/dist/pipeline/segment-plan.d.ts +10 -0
  48. package/dist/pipeline/segment-plan.d.ts.map +1 -0
  49. package/dist/pipeline/segment-plan.js +55 -0
  50. package/dist/pipeline/segment-plan.js.map +1 -0
  51. package/dist/pipeline/subtitle.d.ts +17 -0
  52. package/dist/pipeline/subtitle.d.ts.map +1 -0
  53. package/dist/pipeline/subtitle.js +184 -0
  54. package/dist/pipeline/subtitle.js.map +1 -0
  55. package/dist/pipeline/types.d.ts +98 -0
  56. package/dist/pipeline/types.d.ts.map +1 -0
  57. package/dist/pipeline/types.js +2 -0
  58. package/dist/pipeline/types.js.map +1 -0
  59. package/package.json +52 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jonathan Graehl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="docs/wordmark-dark.svg">
3
+ <source media="(prefers-color-scheme: light)" srcset="docs/wordmark-light.svg">
4
+ <img alt="playsvideo" src="docs/wordmark-light.svg" width="340">
5
+ </picture>
6
+
7
+ **You may not need VLC.** Play any video file in the browser — no install, no upload.
8
+
9
+ [Try it at playsvideo.com](https://playsvideo.com) &nbsp;|&nbsp; Drop a file. It plays.
10
+
11
+ ---
12
+
13
+ Most video files won't play in a browser — not because the browser can't *decode* the video, but because it can't open the container or handle the audio codec. playsvideo fixes that entirely client-side: it remuxes containers and transcodes audio on the fly, so your MKV with AC-3 audio just works.
14
+
15
+ ### What it handles
16
+
17
+ | | Formats | Notes |
18
+ |---|---|---|
19
+ | **Containers** | MKV, MP4, AVI, TS, WebM | Demuxed and remuxed to fMP4 |
20
+ | **Video** | H.264, H.265, VP9, AV1 | Passthrough — plays ~99% of files (~90% on Firefox; HEVC transcode planned) |
21
+ | **Audio** | AAC, MP3, AC-3, E-AC-3, DTS, FLAC, Opus | Unsupported codecs transcoded to AAC on the fly |
22
+ | **Subtitles** | SRT, ASS/SSA | Extracted and displayed as WebVTT |
23
+
24
+ See [supported media](docs/supported-media.md) for the full codec matrix, browser compatibility, and transcode details.
25
+
26
+ ### How it works
27
+
28
+ ```
29
+ Video file (MKV, MP4, AVI, …)
30
+ → mediabunny demux (streaming, any file size)
31
+ → keyframe-aligned segment plan
32
+ → per segment:
33
+ video passed through or transcoded if needed
34
+ audio transcoded only if needed (AC-3/DTS/FLAC → AAC)
35
+ muxed to fMP4
36
+ → hls.js plays segments on demand
37
+ → subtitles extracted to WebVTT
38
+ ```
39
+
40
+ Video transcode is almost never needed — browsers natively decode the vast majority of video codecs in the wild. When audio transcode is needed, a lightweight 1.5 MB ffmpeg.wasm build is lazy-loaded on demand — a few seconds at a time, entirely in-browser.
41
+
42
+ ### Under the hood
43
+
44
+ The obvious approach — ffmpeg compiled to WebAssembly — can't handle large files (WORKERFS is catastrophically slow, MEMFS can't hold them). The trick is to split the problem:
45
+
46
+ - **[mediabunny](https://github.com/Vanilagy/mediabunny)** — streaming demux/remux in pure TypeScript, works on any size file
47
+ - **[ffmpeg.wasm](https://github.com/nicolo-ribaudo/ffmpeg.wasm)** — only transcodes short audio segments via MEMFS
48
+ - **[hls.js](https://github.com/video-dev/hls.js)** — battle-tested HLS playback via Media Source Extensions
49
+
50
+ Each piece existed separately. Nobody combined them.
51
+
52
+ ### Use as a library
53
+
54
+ > Not yet published to npm. The API below is the current interface.
55
+
56
+ ```ts
57
+ import { PlaysVideoEngine } from 'playsvideo';
58
+
59
+ const video = document.querySelector('video')!;
60
+ const engine = new PlaysVideoEngine(video);
61
+
62
+ engine.addEventListener('ready', (e) => {
63
+ console.log(`${e.detail.totalSegments} segments, ${e.detail.durationSec}s`);
64
+ });
65
+
66
+ engine.loadFile(file); // from drag-and-drop or <input type="file">
67
+ engine.destroy(); // clean up
68
+ ```
69
+
70
+ ### Roadmap
71
+
72
+ - **npm publish** — `npm install playsvideo`
73
+ - **WebCodecs** — replace ffmpeg.wasm audio transcode with `AudioDecoder`/`AudioEncoder` (smaller bundle, lower latency)
74
+ - **Video transcode** — hardware-accelerated decode via `VideoDecoder` for edge-case codecs
75
+
76
+ <details>
77
+ <summary><strong>Development</strong></summary>
78
+
79
+ ```bash
80
+ npm run setup # install deps + download ffmpeg-core.wasm
81
+ npm run dev # vite dev server
82
+ npm run typecheck # tsc --noEmit
83
+ npm run test:unit # fast unit tests
84
+ npm run lint # biome lint
85
+ npm run format # biome format
86
+ npm run test:integration # requires test fixtures in tests/fixtures/
87
+ ```
88
+
89
+ ```
90
+ src/pipeline/ Core modules (demux, mux, segment plan, audio transcode,
91
+ codec probe, playlist, subtitle extraction)
92
+ src/adapters/ Platform adapters (ffmpeg.wasm for browser, node-ffmpeg for tests)
93
+ src/worker.ts Web worker — demux + on-demand segment processing
94
+ src/engine.ts PlaysVideoEngine class (worker, hls.js, subtitles)
95
+ src/pwa-player.ts Browser entry — file picker, drag-and-drop
96
+ tests/ Unit, integration, and e2e (Playwright) tests
97
+ ```
98
+
99
+ </details>
@@ -0,0 +1,15 @@
1
+ import type { FfmpegRunner } from '../pipeline/types.js';
2
+ export declare class NodeFfmpegRunner implements FfmpegRunner {
3
+ private ffmpegPath;
4
+ private dir;
5
+ constructor(dir: string, ffmpegPath?: string);
6
+ writeInput(name: string, data: Uint8Array): Promise<void>;
7
+ readOutput(name: string): Promise<Uint8Array>;
8
+ deleteFile(name: string): Promise<void>;
9
+ run(args: string[]): Promise<{
10
+ exitCode: number;
11
+ stderr: string;
12
+ }>;
13
+ }
14
+ export declare function makeTempDir(prefix?: string): Promise<string>;
15
+ //# sourceMappingURL=node-ffmpeg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-ffmpeg.d.ts","sourceRoot":"","sources":["../../src/adapters/node-ffmpeg.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,qBAAa,gBAAiB,YAAW,YAAY;IACnD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,EAAE,MAAM,EAAE,UAAU,SAAW;IAKxC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAgBzE;AAED,wBAAsB,WAAW,CAAC,MAAM,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEzE"}
@@ -0,0 +1,35 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { mkdtemp, readFile, unlink, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ export class NodeFfmpegRunner {
6
+ ffmpegPath;
7
+ dir;
8
+ constructor(dir, ffmpegPath = 'ffmpeg') {
9
+ this.dir = dir;
10
+ this.ffmpegPath = ffmpegPath;
11
+ }
12
+ async writeInput(name, data) {
13
+ await writeFile(join(this.dir, name), data);
14
+ }
15
+ async readOutput(name) {
16
+ return new Uint8Array(await readFile(join(this.dir, name)));
17
+ }
18
+ async deleteFile(name) {
19
+ await unlink(join(this.dir, name)).catch(() => { });
20
+ }
21
+ async run(args) {
22
+ return new Promise((resolve) => {
23
+ execFile(this.ffmpegPath, args, { maxBuffer: 100 * 1024 * 1024, cwd: this.dir }, (error, _stdout, stderr) => {
24
+ resolve({
25
+ exitCode: error?.code !== undefined ? (typeof error.code === 'number' ? error.code : 1) : 0,
26
+ stderr: stderr || '',
27
+ });
28
+ });
29
+ });
30
+ }
31
+ }
32
+ export async function makeTempDir(prefix = 'playsvideo-') {
33
+ return mkdtemp(join(tmpdir(), prefix));
34
+ }
35
+ //# sourceMappingURL=node-ffmpeg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-ffmpeg.js","sourceRoot":"","sources":["../../src/adapters/node-ffmpeg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,OAAO,gBAAgB;IACnB,UAAU,CAAS;IACnB,GAAG,CAAS;IAEpB,YAAY,GAAW,EAAE,UAAU,GAAG,QAAQ;QAC5C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,IAAgB;QAC7C,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAc;QACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,QAAQ,CACN,IAAI,CAAC,UAAU,EACf,IAAI,EACJ,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAC/C,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;gBACzB,OAAO,CAAC;oBACN,QAAQ,EACN,KAAK,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACnF,MAAM,EAAE,MAAM,IAAI,EAAE;iBACrB,CAAC,CAAC;YACL,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAM,GAAG,aAAa;IACtD,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ProbeResult } from '../pipeline/types.js';
2
+ export declare class NodeFfprobeRunner {
3
+ private ffprobePath;
4
+ constructor(ffprobePath?: string);
5
+ probe(inputPath: string): Promise<ProbeResult>;
6
+ verifyDecodable(inputPath: string, ffmpegPath?: string): Promise<{
7
+ ok: boolean;
8
+ stderr: string;
9
+ }>;
10
+ private execJson;
11
+ }
12
+ //# sourceMappingURL=node-ffprobe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-ffprobe.d.ts","sourceRoot":"","sources":["../../src/adapters/node-ffprobe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,sBAAsB,CAAC;AAErE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,SAAY;IAI7B,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA+B9C,eAAe,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,SAAW,GACpB,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAa3C,OAAO,CAAC,QAAQ;CAWjB"}
@@ -0,0 +1,55 @@
1
+ import { execFile } from 'node:child_process';
2
+ export class NodeFfprobeRunner {
3
+ ffprobePath;
4
+ constructor(ffprobePath = 'ffprobe') {
5
+ this.ffprobePath = ffprobePath;
6
+ }
7
+ async probe(inputPath) {
8
+ const stdout = await this.execJson([
9
+ '-v',
10
+ 'error',
11
+ '-print_format',
12
+ 'json',
13
+ '-show_streams',
14
+ '-show_format',
15
+ inputPath,
16
+ ]);
17
+ const data = JSON.parse(stdout);
18
+ const streams = (data.streams || []).map((s) => ({
19
+ index: s.index,
20
+ codecType: s.codec_type,
21
+ codecName: s.codec_name,
22
+ width: s.width,
23
+ height: s.height,
24
+ sampleRate: s.sample_rate ? parseInt(s.sample_rate, 10) : undefined,
25
+ channels: s.channels,
26
+ duration: s.duration ? parseFloat(s.duration) : undefined,
27
+ }));
28
+ return {
29
+ format: data.format?.format_name ?? 'unknown',
30
+ duration: parseFloat(data.format?.duration ?? '0'),
31
+ bitRate: data.format?.bit_rate ? parseFloat(data.format.bit_rate) : undefined,
32
+ streams,
33
+ };
34
+ }
35
+ async verifyDecodable(inputPath, ffmpegPath = 'ffmpeg') {
36
+ return new Promise((resolve) => {
37
+ execFile(ffmpegPath, ['-hide_banner', '-loglevel', 'error', '-i', inputPath, '-f', 'null', '-'], { maxBuffer: 10 * 1024 * 1024 }, (error, _stdout, stderr) => {
38
+ resolve({ ok: !error, stderr: stderr || '' });
39
+ });
40
+ });
41
+ }
42
+ execJson(args) {
43
+ return new Promise((resolve, reject) => {
44
+ execFile(this.ffprobePath, args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
45
+ if (error) {
46
+ reject(new Error(`ffprobe failed: ${stderr || error.message}`));
47
+ }
48
+ else {
49
+ resolve(stdout);
50
+ }
51
+ });
52
+ });
53
+ }
54
+ }
55
+ //# sourceMappingURL=node-ffprobe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-ffprobe.js","sourceRoot":"","sources":["../../src/adapters/node-ffprobe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { SubtitleTrackInfo } from './pipeline/types.js';
2
+ export type EnginePhase = 'idle' | 'demuxing' | 'ready' | 'error';
3
+ export interface ReadyDetail {
4
+ totalSegments: number;
5
+ durationSec: number;
6
+ subtitleTracks: SubtitleTrackInfo[];
7
+ }
8
+ export interface ErrorDetail {
9
+ message: string;
10
+ }
11
+ export interface LoadingDetail {
12
+ file: File;
13
+ }
14
+ interface EngineEventMap {
15
+ ready: CustomEvent<ReadyDetail>;
16
+ error: CustomEvent<ErrorDetail>;
17
+ loading: CustomEvent<LoadingDetail>;
18
+ }
19
+ export declare class PlaysVideoEngine extends EventTarget {
20
+ private video;
21
+ private worker;
22
+ private hls;
23
+ private pendingSegments;
24
+ private playlist;
25
+ private initData;
26
+ private pendingInit;
27
+ private pendingPlaylist;
28
+ private segmentRequestTimes;
29
+ private subtitleBlobUrls;
30
+ private _subtitleTracks;
31
+ private _phase;
32
+ private _totalSegments;
33
+ private _durationSec;
34
+ get phase(): EnginePhase;
35
+ get loading(): boolean;
36
+ get totalSegments(): number;
37
+ get durationSec(): number;
38
+ get subtitleTracks(): SubtitleTrackInfo[];
39
+ constructor(video: HTMLVideoElement);
40
+ loadFile(file: File): void;
41
+ destroy(): void;
42
+ addEventListener<K extends keyof EngineEventMap>(type: K, listener: (ev: EngineEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
43
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
44
+ private handleWorkerMessage;
45
+ private requestSegment;
46
+ private startHls;
47
+ private addSubtitleTrack;
48
+ private removeSubtitleTracks;
49
+ }
50
+ export {};
51
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,iBAAiB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAChC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;CACrC;AAED,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,GAAG,CAAoB;IAG/B,OAAO,CAAC,eAAe,CAGnB;IAGJ,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,WAAW,CAGH;IAChB,OAAO,CAAC,eAAe,CAGP;IAEhB,OAAO,CAAC,mBAAmB,CAA6B;IAGxD,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,eAAe,CAA2B;IAGlD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAAK;IAEzB,IAAI,KAAK,IAAI,WAAW,CAEvB;IACD,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,aAAa,IAAI,MAAM,CAE1B;IACD,IAAI,WAAW,IAAI,MAAM,CAExB;IACD,IAAI,cAAc,IAAI,iBAAiB,EAAE,CAExC;gBAEW,KAAK,EAAE,gBAAgB;IAKnC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAkC1B,OAAO,IAAI,IAAI;IAkBf,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7C,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EACzC,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI;IACP,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI;IASP,OAAO,CAAC,mBAAmB;IAiF3B,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,QAAQ;IAgKhB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,oBAAoB;CAK7B"}