ziplayer 0.2.1 → 0.2.3-dev-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 (43) hide show
  1. package/dist/structures/FilterManager.d.ts +1 -0
  2. package/dist/structures/FilterManager.d.ts.map +1 -1
  3. package/dist/structures/FilterManager.js +49 -10
  4. package/dist/structures/FilterManager.js.map +1 -1
  5. package/dist/structures/Player.d.ts +15 -2
  6. package/dist/structures/Player.d.ts.map +1 -1
  7. package/dist/structures/Player.js +123 -37
  8. package/dist/structures/Player.js.map +1 -1
  9. package/dist/types/extension.d.ts +114 -0
  10. package/dist/types/extension.d.ts.map +1 -0
  11. package/dist/types/extension.js +3 -0
  12. package/dist/types/extension.js.map +1 -0
  13. package/dist/types/fillter.d.ts +44 -0
  14. package/dist/types/fillter.d.ts.map +1 -0
  15. package/dist/types/fillter.js +226 -0
  16. package/dist/types/fillter.js.map +1 -0
  17. package/dist/types/index.d.ts +14 -209
  18. package/dist/types/index.d.ts.map +1 -1
  19. package/dist/types/index.js +17 -223
  20. package/dist/types/index.js.map +1 -1
  21. package/dist/types/plugin.d.ts +58 -0
  22. package/dist/types/plugin.d.ts.map +1 -0
  23. package/dist/types/plugin.js +21 -0
  24. package/dist/types/plugin.js.map +1 -0
  25. package/package.json +7 -2
  26. package/src/structures/FilterManager.ts +303 -262
  27. package/src/structures/Player.ts +135 -41
  28. package/src/types/extension.ts +129 -0
  29. package/src/types/fillter.ts +264 -0
  30. package/src/types/index.ts +15 -443
  31. package/src/types/plugin.ts +57 -0
  32. package/dist/plugins/SoundCloudPlugin.d.ts +0 -22
  33. package/dist/plugins/SoundCloudPlugin.d.ts.map +0 -1
  34. package/dist/plugins/SoundCloudPlugin.js +0 -171
  35. package/dist/plugins/SoundCloudPlugin.js.map +0 -1
  36. package/dist/plugins/SpotifyPlugin.d.ts +0 -26
  37. package/dist/plugins/SpotifyPlugin.d.ts.map +0 -1
  38. package/dist/plugins/SpotifyPlugin.js +0 -183
  39. package/dist/plugins/SpotifyPlugin.js.map +0 -1
  40. package/dist/plugins/YouTubePlugin.d.ts +0 -25
  41. package/dist/plugins/YouTubePlugin.d.ts.map +0 -1
  42. package/dist/plugins/YouTubePlugin.js +0 -314
  43. package/dist/plugins/YouTubePlugin.js.map +0 -1
@@ -1,7 +1,8 @@
1
- import { VoiceConnection } from "@discordjs/voice";
2
1
  import { Readable } from "stream";
3
2
  import { Player } from "../structures/Player";
4
3
  import type { PlayerManager } from "../structures/PlayerManager";
4
+ import type { AudioFilter } from "./fillter";
5
+ import type { SourcePluginLike } from "./plugin";
5
6
 
6
7
  /**
7
8
  * Represents a music track with metadata and streaming information.
@@ -178,42 +179,6 @@ export interface PlayerOptions {
178
179
  filters?: (string | AudioFilter)[];
179
180
  }
180
181
 
181
- /**
182
- * Constructor for a SourcePlugin
183
- *
184
- * @example
185
- * const plugin = new YouTubePlugin();
186
- * console.log(`Plugin: ${plugin.name}`);
187
- */
188
- export type SourcePluginCtor<T extends SourcePlugin = SourcePlugin> = new (...args: any[]) => T;
189
-
190
- /**
191
- * SourcePlugin or SourcePluginCtor
192
- *
193
- * @example
194
- * const plugin = new YouTubePlugin();
195
- * console.log(`Plugin: ${plugin.name}`);
196
- */
197
- export type SourcePluginLike = SourcePlugin | SourcePluginCtor;
198
-
199
- /**
200
- * Configuration options for creating a PlayerManager instance.
201
- *
202
- * @example
203
- * const managerOptions: PlayerManagerOptions = {
204
- * plugins: [
205
- * new YouTubePlugin(),
206
- * new SoundCloudPlugin(),
207
- * new SpotifyPlugin(),
208
- * new TTSPlugin({ defaultLang: "en" })
209
- * ],
210
- * extensions: [
211
- * new voiceExt(null, { lang: "en-US" }),
212
- * new lavalinkExt(null, { nodes: [...] })
213
- * ],
214
- * extractorTimeout: 10000
215
- * };
216
- */
217
182
  export interface PlayerManagerOptions {
218
183
  plugins?: SourcePluginLike[];
219
184
  extensions?: any[];
@@ -259,366 +224,20 @@ export interface SaveOptions {
259
224
  timeout?: number;
260
225
  /** Additional metadata to include */
261
226
  metadata?: Record<string, any>;
227
+ /** Filter */
228
+ filter?: AudioFilter[];
229
+ /** Seek position in milliseconds to start saving from */
230
+ seek?: number;
262
231
  }
263
232
 
264
233
  export type LoopMode = "off" | "track" | "queue";
265
234
 
266
- /**
267
- * Audio filter configuration for applying effects to audio streams.
268
- * Based on FFmpeg audio filters for Discord music bots.
269
- *
270
- * @example
271
- * // Bass boost filter
272
- * const bassFilter: AudioFilter = {
273
- * name: "bassboost",
274
- * ffmpegFilter: "bass=g=10:f=110:w=0.5",
275
- * description: "Tăng âm trầm"
276
- * };
277
- *
278
- * // Nightcore filter (speed + pitch)
279
- * const nightcoreFilter: AudioFilter = {
280
- * name: "nightcore",
281
- * ffmpegFilter: "atempo=1.25,asetrate=44100*1.25",
282
- * description: "Tăng tốc độ và cao độ"
283
- * };
284
- *
285
- * // Custom filter
286
- * const customFilter: AudioFilter = {
287
- * name: "custom",
288
- * ffmpegFilter: "volume=1.5,treble=g=5",
289
- * description: "Tăng âm lượng và âm cao"
290
- * };
291
- */
292
- export interface AudioFilter {
293
- /** Unique name identifier for the filter */
294
- name: string;
295
- /** FFmpeg audio filter string */
296
- ffmpegFilter: string;
297
- /** Human-readable description of the filter */
298
- description: string;
299
- /** Optional category for grouping filters */
300
- category?: string;
301
- /** Optional parameters for dynamic filter generation */
302
- parameters?: Record<string, any>;
303
- }
304
-
305
- /**
306
- * Predefined audio filters commonly used in Discord music bots.
307
- * These filters are based on popular FFmpeg audio filter combinations.
308
- */
309
- export const PREDEFINED_FILTERS: Record<string, AudioFilter> = {
310
- bassboost: {
311
- name: "bassboost",
312
- ffmpegFilter: "bass=g=10:f=110:w=0.5",
313
- description: "Bass Boost",
314
- category: "eq",
315
- },
316
- nightcore: {
317
- name: "nightcore",
318
- ffmpegFilter: "aresample=48000,asetrate=48000*1.5",
319
- description: "Nightcore",
320
- category: "speed",
321
- },
322
- karaoke: {
323
- name: "karaoke",
324
- ffmpegFilter: "stereotools=mlev=0.1",
325
- description: "Karaoke",
326
- category: "vocal",
327
- },
328
- lofi: {
329
- name: "lofi",
330
- ffmpegFilter: "aresample=48000,asetrate=48000*0.9,extrastereo=m=2.5:c=disabled",
331
- description: "Lo-fi",
332
- category: "speed",
333
- },
334
- "8D": {
335
- name: "8D",
336
- ffmpegFilter: "apulsator=hz=0.08",
337
- description: "8D Effect",
338
- category: "effect",
339
- },
340
- vaporwave: {
341
- name: "vaporwave",
342
- ffmpegFilter:
343
- "highpass=f=50, lowpass=f=2750, aresample=48000, asetrate=48000*0.85,bass=g=5:f=110:w=0.6, compand=attacks=0:points=-80/-169|-54/-80|-49.5/-64.6|-41.1/-41.1|-25.8/-15|-10.8/-4.5|0/0|20/8.3",
344
- description: "Vaporwave",
345
- category: "speed",
346
- },
347
- bathroom: {
348
- name: "bathroom",
349
- ffmpegFilter:
350
- "highpass=f=10, lowpass=f=400, aresample=44100, asetrate=44100*0.85,bass=g=4:f=110:w=0.6, alimiter=1, compand=attacks=0:points=-80/-169|-54/-80|-49.5/-64.6|-41.1/-41.1|-25.8/-15|-10.8/-4.5|0/0|20/8.3",
351
- description: "Bathroom",
352
- category: "speed",
353
- },
354
- expander: {
355
- name: "expander",
356
- ffmpegFilter: "compand=attacks=0:points=-80/-169|-54/-80|-49.5/-64.6|-41.1/-41.1|-25.8/-15|-10.8/-4.5|0/0|20/8.3",
357
- description: "Expander",
358
- category: "speed",
359
- },
360
- reverse: {
361
- name: "reverse",
362
- ffmpegFilter: "areverse",
363
- description: "Reverse",
364
- category: "effect",
365
- },
366
- echo: {
367
- name: "echo",
368
- ffmpegFilter: "aecho=0.8:0.88:60:0.4",
369
- description: "Echo",
370
- category: "effect",
371
- },
372
- trebleboost: {
373
- name: "trebleboost",
374
- ffmpegFilter: "treble=g=10:f=3000:w=0.5",
375
- description: "Treble Boost",
376
- category: "eq",
377
- },
378
-
379
- chorus: {
380
- name: "chorus",
381
- ffmpegFilter: "chorus=0.5:0.9:50:0.4:0.25:2",
382
- description: "Chorus",
383
- category: "effect",
384
- },
385
- flanger: {
386
- name: "flanger",
387
- ffmpegFilter: "flanger=delay=10:depth=2:regen=0:width=71:speed=0.5",
388
- description: "Flanger",
389
- category: "effect",
390
- },
391
- phaser: {
392
- name: "phaser",
393
- ffmpegFilter: "aphaser=in_gain=0.4:out_gain=0.74:delay=3.0:decay=0.4:speed=0.5",
394
- description: "Phaser",
395
- category: "effect",
396
- },
397
- tremolo: {
398
- name: "tremolo",
399
- ffmpegFilter: "tremolo=f=4.0:d=0.5",
400
- description: "Tremolo",
401
- category: "effect",
402
- },
403
- vibrato: {
404
- name: "vibrato",
405
- ffmpegFilter: "vibrato=f=5.5:d=0.5",
406
- description: "Vibrato",
407
- category: "effect",
408
- },
409
- normalize: {
410
- name: "normalize",
411
- ffmpegFilter: "loudnorm",
412
- description: "Normalize",
413
- category: "volume",
414
- },
415
- compressor: {
416
- name: "compressor",
417
- ffmpegFilter: "compand=points=-80/-105|-62/-80|-15.4/-15.4|0/-12|20/-7.6",
418
- description: "Compressor",
419
- category: "dynamics",
420
- },
421
- limiter: {
422
- name: "limiter",
423
- ffmpegFilter: "alimiter=level_in=1:level_out=0.8:limit=0.9",
424
- description: "Limiter",
425
- category: "dynamics",
426
- },
427
- gate: {
428
- name: "gate",
429
- ffmpegFilter: "agate=threshold=0.01:ratio=2:attack=1:release=100",
430
- description: "Gate",
431
- category: "dynamics",
432
- },
433
- lowpass: {
434
- name: "lowpass",
435
- ffmpegFilter: "lowpass=f=3000",
436
- description: "Lowpass",
437
- category: "filter",
438
- },
439
- highpass: {
440
- name: "highpass",
441
- ffmpegFilter: "highpass=f=200",
442
- description: "Highpass",
443
- category: "filter",
444
- },
445
- bandpass: {
446
- name: "bandpass",
447
- ffmpegFilter: "bandpass=f=1000:csg=1",
448
- description: "Bandpass",
449
- category: "filter",
450
- },
451
- allpass: {
452
- name: "allpass",
453
- ffmpegFilter: "allpass=f=1000:width_type=h:width=200",
454
- description: "Allpass",
455
- category: "filter",
456
- },
457
- equalizer: {
458
- name: "equalizer",
459
- ffmpegFilter: "equalizer=f=1000:width_type=h:width=200:g=5",
460
- description: "Equalizer",
461
- category: "eq",
462
- },
463
- reverb: {
464
- name: "reverb",
465
- ffmpegFilter: "aecho=0.8:0.88:60:0.4",
466
- description: "Reverb",
467
- category: "effect",
468
- },
469
- delay: {
470
- name: "delay",
471
- ffmpegFilter: "aecho=0.8:0.9:1000:0.3",
472
- description: "Delay",
473
- category: "effect",
474
- },
475
- distortion: {
476
- name: "distortion",
477
- ffmpegFilter: "acrusher=bits=8:mode=log:aa=1",
478
- description: "Distortion",
479
- category: "effect",
480
- },
481
- bitcrusher: {
482
- name: "bitcrusher",
483
- ffmpegFilter: "acrusher=bits=8:mode=log:aa=1",
484
- description: "Bitcrusher",
485
- category: "effect",
486
- },
487
- robot: {
488
- name: "robot",
489
- ffmpegFilter: "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=512:overlap=0.75",
490
- description: "Robot",
491
- category: "vocal",
492
- },
493
- slow: {
494
- name: "slow",
495
- ffmpegFilter: "atempo=0.5",
496
- description: "Slow",
497
- category: "speed",
498
- },
499
- fast: {
500
- name: "fast",
501
- ffmpegFilter: "atempo=2.0",
502
- description: "Fast",
503
- category: "speed",
504
- },
505
- mono: {
506
- name: "mono",
507
- ffmpegFilter: "pan=mono|c0=0.5*c0+0.5*c1",
508
- description: "Mono",
509
- category: "channel",
510
- },
511
- stereo: {
512
- name: "stereo",
513
- ffmpegFilter: "pan=stereo|c0<c0+c1+c2+c3+c4+c5|c1<c0+c1+c2+c3+c4+c5",
514
- description: "Stereo",
515
- category: "channel",
516
- },
517
- haas: {
518
- name: "haas",
519
- ffmpegFilter: "haas",
520
- description: "Haas",
521
- category: "dynamics",
522
- },
523
- fadein: {
524
- name: "fadein",
525
- ffmpegFilter: "afade=t=in:ss=0:d=5",
526
- description: "Fadein",
527
- category: "effect",
528
- },
529
- };
530
-
531
- /**
532
- * Context for the extension
533
- *
534
- * @example
535
- * const context: ExtensionContext = {
536
- * player: player,
537
- * manager: manager
538
- * };
539
- */
540
- export interface ExtensionContext {
541
- player: Player;
542
- manager: PlayerManager;
543
- }
544
-
545
- /**
546
- * Request for the extension to play a track
547
- *
548
- * @example
549
- * const request: ExtensionPlayRequest = {
550
- * query: "Song Name",
551
- * requestedBy: "user123"
552
- * };
553
- */
554
- export interface ExtensionPlayRequest {
555
- query: string | Track;
556
- requestedBy?: string;
557
- }
558
-
559
- /**
560
- * Response for the extension to play a track
561
- *
562
- * @example
563
- * const response: ExtensionPlayResponse = {
564
- * handled: true,
565
- * query: "Song Name",
566
- * requestedBy: "user123"
567
- * };
568
- */
569
- export interface ExtensionPlayResponse {
570
- handled?: boolean;
571
- query?: string | Track;
572
- requestedBy?: string;
573
- tracks?: Track[];
574
- isPlaylist?: boolean;
575
- success?: boolean;
576
- error?: Error;
577
- }
578
-
579
- /**
580
- * Payload for the extension to play a track
581
- *
582
- * @example
583
- * const payload: ExtensionAfterPlayPayload = {
584
- * success: true,
585
- * query: "Song Name",
586
- * requestedBy: "user123"
587
- * };
588
- */
589
- export interface ExtensionAfterPlayPayload {
590
- success: boolean;
591
- query: string | Track;
592
- requestedBy?: string;
593
- tracks?: Track[];
594
- isPlaylist?: boolean;
595
- error?: Error;
596
- }
597
-
598
- /**
599
- * Request for the extension to stream a track
600
- *
601
- * @example
602
- * const request: ExtensionStreamRequest = {
603
- * track: track
604
- * };
605
- */
606
- export interface ExtensionStreamRequest {
607
- track: Track;
608
- }
609
-
610
- /**
611
- * Request for the extension to search for a track
612
- *
613
- * @example
614
- * const request: ExtensionSearchRequest = {
615
- * query: "Song Name",
616
- * requestedBy: "user123"
617
- * };
618
- */
619
- export interface ExtensionSearchRequest {
620
- query: string;
621
- requestedBy: string;
235
+ export interface VoiceChannel {
236
+ id: string;
237
+ guildId: string;
238
+ type: any;
239
+ guild: any;
240
+ // Placeholder for VoiceConnection properties and methods
622
241
  }
623
242
 
624
243
  /**
@@ -748,54 +367,7 @@ export interface PlayerEvents {
748
367
  /** Emitted when all filters are cleared */
749
368
  filtersCleared: [];
750
369
  }
751
- /**
752
- * Plugin interface
753
- *
754
- * @example
755
- * const plugin: SourcePlugin = {
756
- * name: "YouTube",
757
- * version: "1.0.0"
758
- * };
759
- */
760
- export interface SourcePlugin {
761
- name: string;
762
- version: string;
763
- canHandle(query: string): boolean;
764
- search(query: string, requestedBy: string): Promise<SearchResult>;
765
- getStream(track: Track): Promise<StreamInfo>;
766
- getRelatedTracks?(track: string | number, opts?: { limit?: number; offset?: number }): Promise<Track[]>;
767
- validate?(url: string): boolean;
768
- extractPlaylist?(url: string, requestedBy: string): Promise<Track[]>;
769
- }
770
370
 
771
- /**
772
- * Extension interface
773
- *
774
- * @example
775
- * const extension: SourceExtension = {
776
- * name: "YouTube",
777
- * version: "1.0.0"
778
- * };
779
- */
780
- export interface SourceExtension {
781
- name: string;
782
- version: string;
783
- connection?: VoiceConnection;
784
- player: Player | null;
785
- active(alas: any): boolean | Promise<boolean>;
786
- onRegister?(context: ExtensionContext): void | Promise<void>;
787
- onDestroy?(context: ExtensionContext): void | Promise<void>;
788
- beforePlay?(
789
- context: ExtensionContext,
790
- payload: ExtensionPlayRequest,
791
- ): Promise<ExtensionPlayResponse | void> | ExtensionPlayResponse | void;
792
- afterPlay?(context: ExtensionContext, payload: ExtensionAfterPlayPayload): Promise<void> | void;
793
- provideSearch?(
794
- context: ExtensionContext,
795
- payload: ExtensionSearchRequest,
796
- ): Promise<SearchResult | null | undefined> | SearchResult | null | undefined;
797
- provideStream?(
798
- context: ExtensionContext,
799
- payload: ExtensionStreamRequest,
800
- ): Promise<StreamInfo | null | undefined> | StreamInfo | null | undefined;
801
- }
371
+ export * from "./fillter";
372
+ export * from "./plugin";
373
+ export * from "./extension";
@@ -0,0 +1,57 @@
1
+ import type { SearchResult, StreamInfo, Track } from ".";
2
+ /**
3
+ * Plugin interface
4
+ *
5
+ * @example
6
+ * const plugin: SourcePlugin = {
7
+ * name: "YouTube",
8
+ * version: "1.0.0"
9
+ * };
10
+ */
11
+ export interface SourcePlugin {
12
+ name: string;
13
+ version: string;
14
+ canHandle(query: string): boolean;
15
+ search(query: string, requestedBy: string): Promise<SearchResult>;
16
+ getStream(track: Track): Promise<StreamInfo>;
17
+ getRelatedTracks?(track: string | number, opts?: { limit?: number; offset?: number }): Promise<Track[]>;
18
+ validate?(url: string): boolean;
19
+ extractPlaylist?(url: string, requestedBy: string): Promise<Track[]>;
20
+ }
21
+
22
+ /**
23
+ * Constructor for a SourcePlugin
24
+ *
25
+ * @example
26
+ * const plugin = new YouTubePlugin();
27
+ * console.log(`Plugin: ${plugin.name}`);
28
+ */
29
+ export type SourcePluginCtor<T extends SourcePlugin = SourcePlugin> = new (...args: any[]) => T;
30
+
31
+ /**
32
+ * SourcePlugin or SourcePluginCtor
33
+ *
34
+ * @example
35
+ * const plugin = new YouTubePlugin();
36
+ * console.log(`Plugin: ${plugin.name}`);
37
+ */
38
+ export type SourcePluginLike = SourcePlugin | SourcePluginCtor;
39
+
40
+ /**
41
+ * Configuration options for creating a PlayerManager instance.
42
+ *
43
+ * @example
44
+ * const managerOptions: PlayerManagerOptions = {
45
+ * plugins: [
46
+ * new YouTubePlugin(),
47
+ * new SoundCloudPlugin(),
48
+ * new SpotifyPlugin(),
49
+ * new TTSPlugin({ defaultLang: "en" })
50
+ * ],
51
+ * extensions: [
52
+ * new voiceExt(null, { lang: "en-US" }),
53
+ * new lavalinkExt(null, { nodes: [...] })
54
+ * ],
55
+ * extractorTimeout: 10000
56
+ * };
57
+ */
@@ -1,22 +0,0 @@
1
- import { BasePlugin } from "./BasePlugin";
2
- import { Track, SearchResult, StreamInfo } from "../types";
3
- export declare class SoundCloudPlugin extends BasePlugin {
4
- name: string;
5
- version: string;
6
- private client;
7
- private ready;
8
- constructor();
9
- private init;
10
- canHandle(query: string): boolean;
11
- validate(url: string): boolean;
12
- search(query: string, requestedBy: string): Promise<SearchResult>;
13
- getStream(track: Track): Promise<StreamInfo>;
14
- getRelatedTracks(trackURL: string | number, opts?: {
15
- limit?: number;
16
- offset?: number;
17
- history?: Track[];
18
- }): Promise<Track[]>;
19
- getFallback(track: Track): Promise<StreamInfo>;
20
- extractPlaylist(url: string, requestedBy: string): Promise<Track[]>;
21
- }
22
- //# sourceMappingURL=SoundCloudPlugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SoundCloudPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/SoundCloudPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3D,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,IAAI,SAAgB;IACpB,OAAO,SAAW;IAClB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,KAAK,CAAgB;;YAOf,IAAI;IAKlB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAOjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIxB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsEjE,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IAmB5C,gBAAgB,CACpB,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAA;KAAO,GAChE,OAAO,CAAC,KAAK,EAAE,CAAC;IAoCb,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IAS9C,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;CAsB1E"}