sonilo 0.12.0 → 0.14.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/README.md +54 -0
- package/dist/index.cjs +48 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +48 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -110,6 +110,14 @@ interface VideoToMusicParams {
|
|
|
110
110
|
* by `submit()`) — only meaningful via `submit()`; `stream()`/`generate()`
|
|
111
111
|
* never send it, since they always request a plain stream. */
|
|
112
112
|
variantsNum?: number;
|
|
113
|
+
/** How strongly the generated music follows the prompt (0-1, API default
|
|
114
|
+
* 0.5). Lower values let the video lead; higher values follow the prompt
|
|
115
|
+
* more literally. Free of charge. A generation-time knob, not a
|
|
116
|
+
* finalize-time one, so it works on the plain stream as well as async —
|
|
117
|
+
* no `mode` gating. Omitted from the wire when unset so the server
|
|
118
|
+
* default applies; `0` is a meaningful value and is sent. Out-of-range
|
|
119
|
+
* values are rejected server-side with a 422. */
|
|
120
|
+
promptInfluence?: number;
|
|
113
121
|
}
|
|
114
122
|
/** One service's free-trial allowance. `remaining` is already floored at 0,
|
|
115
123
|
* so it is safe to compare directly. */
|
|
@@ -329,6 +337,12 @@ interface VideoToVideoMusicParams {
|
|
|
329
337
|
* by the free trial. This endpoint is always async, so no extra `mode`
|
|
330
338
|
* gating applies. The result's `videos[]` gets one entry per variant. */
|
|
331
339
|
variantsNum?: number;
|
|
340
|
+
/** How strongly the generated music follows the prompt (0-1, API default
|
|
341
|
+
* 0.5). Lower values let the video lead; higher values follow the prompt
|
|
342
|
+
* more literally. Free of charge. Omitted from the wire when unset so the
|
|
343
|
+
* server default applies; `0` is a meaningful value and is sent.
|
|
344
|
+
* Out-of-range values are rejected server-side with a 422. */
|
|
345
|
+
promptInfluence?: number;
|
|
332
346
|
}
|
|
333
347
|
interface VideoToVideoSfxParams {
|
|
334
348
|
video?: VideoInput;
|
|
@@ -438,6 +452,34 @@ interface SoundResult extends BaseTaskResult {
|
|
|
438
452
|
/** One entry per variant; see `SoundOutputEntry`. */
|
|
439
453
|
outputs?: SoundOutputEntry[];
|
|
440
454
|
}
|
|
455
|
+
/** Params for `audioDucking`: mix an existing music bed under an existing
|
|
456
|
+
* voice track, ducking the music wherever the voice speaks. Exactly one of
|
|
457
|
+
* `voice`/`voiceUrl` and exactly one of `music`/`musicUrl`; a local input and
|
|
458
|
+
* a URL may be mixed across the two.
|
|
459
|
+
*
|
|
460
|
+
* The voice may be audio or video (`VideoInput` is the SDK's generic media
|
|
461
|
+
* input union) — a video's own audio track becomes the voice, and the ducked
|
|
462
|
+
* mix is re-muxed back into a new video. The music must be audio: the backend
|
|
463
|
+
* never probes it for a video stream, so a video there would be silently
|
|
464
|
+
* mishandled. Each input is capped at 360 seconds server-side. */
|
|
465
|
+
interface AudioDuckingParams {
|
|
466
|
+
voice?: VideoInput;
|
|
467
|
+
voiceUrl?: string;
|
|
468
|
+
music?: VideoInput;
|
|
469
|
+
musicUrl?: string;
|
|
470
|
+
}
|
|
471
|
+
/** Result of an `audioDucking` task (`tasks.get`) or its final state
|
|
472
|
+
* (`generate`). Same flat envelope as `SoundResult`, but a ducking task
|
|
473
|
+
* renders exactly one artifact and no stems: a `.wav` (`output_type`
|
|
474
|
+
* "audio"), or a `.mp4` with the ducked mix re-muxed in (`output_type`
|
|
475
|
+
* "video") when the voice input was a video. Pass `output_url` to
|
|
476
|
+
* `download()`. */
|
|
477
|
+
interface DuckingResult extends BaseTaskResult {
|
|
478
|
+
output_url?: string;
|
|
479
|
+
output_type?: "audio" | "video";
|
|
480
|
+
output_bytes?: number;
|
|
481
|
+
duration_seconds?: number;
|
|
482
|
+
}
|
|
441
483
|
/**
|
|
442
484
|
* A target language for /v1/dubbing. The union stays open (`string & {}`) so a
|
|
443
485
|
* language added server-side still type-checks against an older SDK — the
|
|
@@ -582,6 +624,21 @@ declare class VideoToVideoSound {
|
|
|
582
624
|
generate(params: VideoToVideoSoundParams, opts?: WaitOptions): Promise<SoundResult>;
|
|
583
625
|
}
|
|
584
626
|
|
|
627
|
+
/** Duck an existing music bed under a voice track. Async only (202 + poll).
|
|
628
|
+
*
|
|
629
|
+
* Both inputs are user-supplied — nothing is generated here. The voice may be
|
|
630
|
+
* audio OR a video: the backend extracts a video's audio track, ducks the
|
|
631
|
+
* music under it, and re-muxes the ducked mix back into a new video (the
|
|
632
|
+
* result's `output_type` announces which came back). The music must be audio —
|
|
633
|
+
* the backend never probes it for a video stream, so a video there would be
|
|
634
|
+
* silently mishandled. */
|
|
635
|
+
declare class AudioDucking {
|
|
636
|
+
private readonly client;
|
|
637
|
+
constructor(client: SoniloClient);
|
|
638
|
+
submit(params: AudioDuckingParams): Promise<SfxTask>;
|
|
639
|
+
generate(params: AudioDuckingParams, opts?: WaitOptions): Promise<DuckingResult>;
|
|
640
|
+
}
|
|
641
|
+
|
|
585
642
|
/** Dub a video into one or more target languages. Async only; the result
|
|
586
643
|
* carries a language → dubbed-video-URL map under `outputs`. */
|
|
587
644
|
declare class Dubbing {
|
|
@@ -628,6 +685,7 @@ declare class SoniloClient {
|
|
|
628
685
|
readonly videoToVideoSfx: VideoToVideoSfx;
|
|
629
686
|
readonly videoToSound: VideoToSound;
|
|
630
687
|
readonly videoToVideoSound: VideoToVideoSound;
|
|
688
|
+
readonly audioDucking: AudioDucking;
|
|
631
689
|
readonly dubbing: Dubbing;
|
|
632
690
|
constructor(options?: SoniloClientOptions);
|
|
633
691
|
/**
|
|
@@ -710,6 +768,6 @@ declare class RequestTimeoutError extends SoniloError {
|
|
|
710
768
|
declare function download(media: SfxMedia | string | undefined, fetchFn?: typeof globalThis.fetch, timeout?: number): Promise<Uint8Array>;
|
|
711
769
|
|
|
712
770
|
/** The SDK's own version. Generated by scripts/sync-versions.mjs — do not edit. */
|
|
713
|
-
declare const VERSION = "0.
|
|
771
|
+
declare const VERSION = "0.14.0";
|
|
714
772
|
|
|
715
|
-
export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
|
|
773
|
+
export { APIError, type AccountServices, type AudioChunkEvent, type AudioDuckingParams, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type DuckingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
|
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,14 @@ interface VideoToMusicParams {
|
|
|
110
110
|
* by `submit()`) — only meaningful via `submit()`; `stream()`/`generate()`
|
|
111
111
|
* never send it, since they always request a plain stream. */
|
|
112
112
|
variantsNum?: number;
|
|
113
|
+
/** How strongly the generated music follows the prompt (0-1, API default
|
|
114
|
+
* 0.5). Lower values let the video lead; higher values follow the prompt
|
|
115
|
+
* more literally. Free of charge. A generation-time knob, not a
|
|
116
|
+
* finalize-time one, so it works on the plain stream as well as async —
|
|
117
|
+
* no `mode` gating. Omitted from the wire when unset so the server
|
|
118
|
+
* default applies; `0` is a meaningful value and is sent. Out-of-range
|
|
119
|
+
* values are rejected server-side with a 422. */
|
|
120
|
+
promptInfluence?: number;
|
|
113
121
|
}
|
|
114
122
|
/** One service's free-trial allowance. `remaining` is already floored at 0,
|
|
115
123
|
* so it is safe to compare directly. */
|
|
@@ -329,6 +337,12 @@ interface VideoToVideoMusicParams {
|
|
|
329
337
|
* by the free trial. This endpoint is always async, so no extra `mode`
|
|
330
338
|
* gating applies. The result's `videos[]` gets one entry per variant. */
|
|
331
339
|
variantsNum?: number;
|
|
340
|
+
/** How strongly the generated music follows the prompt (0-1, API default
|
|
341
|
+
* 0.5). Lower values let the video lead; higher values follow the prompt
|
|
342
|
+
* more literally. Free of charge. Omitted from the wire when unset so the
|
|
343
|
+
* server default applies; `0` is a meaningful value and is sent.
|
|
344
|
+
* Out-of-range values are rejected server-side with a 422. */
|
|
345
|
+
promptInfluence?: number;
|
|
332
346
|
}
|
|
333
347
|
interface VideoToVideoSfxParams {
|
|
334
348
|
video?: VideoInput;
|
|
@@ -438,6 +452,34 @@ interface SoundResult extends BaseTaskResult {
|
|
|
438
452
|
/** One entry per variant; see `SoundOutputEntry`. */
|
|
439
453
|
outputs?: SoundOutputEntry[];
|
|
440
454
|
}
|
|
455
|
+
/** Params for `audioDucking`: mix an existing music bed under an existing
|
|
456
|
+
* voice track, ducking the music wherever the voice speaks. Exactly one of
|
|
457
|
+
* `voice`/`voiceUrl` and exactly one of `music`/`musicUrl`; a local input and
|
|
458
|
+
* a URL may be mixed across the two.
|
|
459
|
+
*
|
|
460
|
+
* The voice may be audio or video (`VideoInput` is the SDK's generic media
|
|
461
|
+
* input union) — a video's own audio track becomes the voice, and the ducked
|
|
462
|
+
* mix is re-muxed back into a new video. The music must be audio: the backend
|
|
463
|
+
* never probes it for a video stream, so a video there would be silently
|
|
464
|
+
* mishandled. Each input is capped at 360 seconds server-side. */
|
|
465
|
+
interface AudioDuckingParams {
|
|
466
|
+
voice?: VideoInput;
|
|
467
|
+
voiceUrl?: string;
|
|
468
|
+
music?: VideoInput;
|
|
469
|
+
musicUrl?: string;
|
|
470
|
+
}
|
|
471
|
+
/** Result of an `audioDucking` task (`tasks.get`) or its final state
|
|
472
|
+
* (`generate`). Same flat envelope as `SoundResult`, but a ducking task
|
|
473
|
+
* renders exactly one artifact and no stems: a `.wav` (`output_type`
|
|
474
|
+
* "audio"), or a `.mp4` with the ducked mix re-muxed in (`output_type`
|
|
475
|
+
* "video") when the voice input was a video. Pass `output_url` to
|
|
476
|
+
* `download()`. */
|
|
477
|
+
interface DuckingResult extends BaseTaskResult {
|
|
478
|
+
output_url?: string;
|
|
479
|
+
output_type?: "audio" | "video";
|
|
480
|
+
output_bytes?: number;
|
|
481
|
+
duration_seconds?: number;
|
|
482
|
+
}
|
|
441
483
|
/**
|
|
442
484
|
* A target language for /v1/dubbing. The union stays open (`string & {}`) so a
|
|
443
485
|
* language added server-side still type-checks against an older SDK — the
|
|
@@ -582,6 +624,21 @@ declare class VideoToVideoSound {
|
|
|
582
624
|
generate(params: VideoToVideoSoundParams, opts?: WaitOptions): Promise<SoundResult>;
|
|
583
625
|
}
|
|
584
626
|
|
|
627
|
+
/** Duck an existing music bed under a voice track. Async only (202 + poll).
|
|
628
|
+
*
|
|
629
|
+
* Both inputs are user-supplied — nothing is generated here. The voice may be
|
|
630
|
+
* audio OR a video: the backend extracts a video's audio track, ducks the
|
|
631
|
+
* music under it, and re-muxes the ducked mix back into a new video (the
|
|
632
|
+
* result's `output_type` announces which came back). The music must be audio —
|
|
633
|
+
* the backend never probes it for a video stream, so a video there would be
|
|
634
|
+
* silently mishandled. */
|
|
635
|
+
declare class AudioDucking {
|
|
636
|
+
private readonly client;
|
|
637
|
+
constructor(client: SoniloClient);
|
|
638
|
+
submit(params: AudioDuckingParams): Promise<SfxTask>;
|
|
639
|
+
generate(params: AudioDuckingParams, opts?: WaitOptions): Promise<DuckingResult>;
|
|
640
|
+
}
|
|
641
|
+
|
|
585
642
|
/** Dub a video into one or more target languages. Async only; the result
|
|
586
643
|
* carries a language → dubbed-video-URL map under `outputs`. */
|
|
587
644
|
declare class Dubbing {
|
|
@@ -628,6 +685,7 @@ declare class SoniloClient {
|
|
|
628
685
|
readonly videoToVideoSfx: VideoToVideoSfx;
|
|
629
686
|
readonly videoToSound: VideoToSound;
|
|
630
687
|
readonly videoToVideoSound: VideoToVideoSound;
|
|
688
|
+
readonly audioDucking: AudioDucking;
|
|
631
689
|
readonly dubbing: Dubbing;
|
|
632
690
|
constructor(options?: SoniloClientOptions);
|
|
633
691
|
/**
|
|
@@ -710,6 +768,6 @@ declare class RequestTimeoutError extends SoniloError {
|
|
|
710
768
|
declare function download(media: SfxMedia | string | undefined, fetchFn?: typeof globalThis.fetch, timeout?: number): Promise<Uint8Array>;
|
|
711
769
|
|
|
712
770
|
/** The SDK's own version. Generated by scripts/sync-versions.mjs — do not edit. */
|
|
713
|
-
declare const VERSION = "0.
|
|
771
|
+
declare const VERSION = "0.14.0";
|
|
714
772
|
|
|
715
|
-
export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
|
|
773
|
+
export { APIError, type AccountServices, type AudioChunkEvent, type AudioDuckingParams, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type DuckingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
|
package/dist/index.js
CHANGED
|
@@ -392,6 +392,9 @@ var VideoToMusic = class {
|
|
|
392
392
|
if (params.segments !== void 0) {
|
|
393
393
|
form.set("segments", JSON.stringify(params.segments));
|
|
394
394
|
}
|
|
395
|
+
if (params.promptInfluence !== void 0) {
|
|
396
|
+
form.set("prompt_influence", String(params.promptInfluence));
|
|
397
|
+
}
|
|
395
398
|
const res = await this.client.request(
|
|
396
399
|
"/v1/video-to-music",
|
|
397
400
|
{ method: "POST", body: form, signal: params.signal },
|
|
@@ -452,6 +455,9 @@ var VideoToMusic = class {
|
|
|
452
455
|
if (params.variantsNum !== void 0) {
|
|
453
456
|
form.set("variants_num", String(params.variantsNum));
|
|
454
457
|
}
|
|
458
|
+
if (params.promptInfluence !== void 0) {
|
|
459
|
+
form.set("prompt_influence", String(params.promptInfluence));
|
|
460
|
+
}
|
|
455
461
|
const res = await this.client.request("/v1/video-to-music", {
|
|
456
462
|
method: "POST",
|
|
457
463
|
body: form
|
|
@@ -548,6 +554,9 @@ var VideoToVideoMusic = class {
|
|
|
548
554
|
if (params.variantsNum !== void 0) {
|
|
549
555
|
form.set("variants_num", String(params.variantsNum));
|
|
550
556
|
}
|
|
557
|
+
if (params.promptInfluence !== void 0) {
|
|
558
|
+
form.set("prompt_influence", String(params.promptInfluence));
|
|
559
|
+
}
|
|
551
560
|
const res = await this.client.request("/v1/video-to-video-music", {
|
|
552
561
|
method: "POST",
|
|
553
562
|
body: form
|
|
@@ -661,6 +670,43 @@ var VideoToVideoSound = class {
|
|
|
661
670
|
}
|
|
662
671
|
};
|
|
663
672
|
|
|
673
|
+
// src/resources/audioDucking.ts
|
|
674
|
+
var AudioDucking = class {
|
|
675
|
+
constructor(client) {
|
|
676
|
+
this.client = client;
|
|
677
|
+
}
|
|
678
|
+
async submit(params) {
|
|
679
|
+
if (params.voice === void 0 === (params.voiceUrl === void 0)) {
|
|
680
|
+
throw new SoniloError("Provide exactly one of voice or voiceUrl");
|
|
681
|
+
}
|
|
682
|
+
if (params.music === void 0 === (params.musicUrl === void 0)) {
|
|
683
|
+
throw new SoniloError("Provide exactly one of music or musicUrl");
|
|
684
|
+
}
|
|
685
|
+
const form = new FormData();
|
|
686
|
+
if (params.voice !== void 0) {
|
|
687
|
+
const { blob, filename } = await toUploadBlob(params.voice);
|
|
688
|
+
form.set("voice_file", blob, filename);
|
|
689
|
+
} else {
|
|
690
|
+
form.set("voice_url", params.voiceUrl);
|
|
691
|
+
}
|
|
692
|
+
if (params.music !== void 0) {
|
|
693
|
+
const { blob, filename } = await toUploadBlob(params.music);
|
|
694
|
+
form.set("music_file", blob, filename);
|
|
695
|
+
} else {
|
|
696
|
+
form.set("music_url", params.musicUrl);
|
|
697
|
+
}
|
|
698
|
+
const res = await this.client.request("/v1/audio-ducking", {
|
|
699
|
+
method: "POST",
|
|
700
|
+
body: form
|
|
701
|
+
});
|
|
702
|
+
return await res.json();
|
|
703
|
+
}
|
|
704
|
+
async generate(params, opts) {
|
|
705
|
+
const task = await this.submit(params);
|
|
706
|
+
return this.client.tasks.wait(task.task_id, opts);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
664
710
|
// src/resources/dubbing.ts
|
|
665
711
|
async function buildDubbingForm(params) {
|
|
666
712
|
if (params.video === void 0 === (params.videoUrl === void 0)) {
|
|
@@ -705,7 +751,7 @@ var Dubbing = class {
|
|
|
705
751
|
};
|
|
706
752
|
|
|
707
753
|
// src/version.ts
|
|
708
|
-
var VERSION = "0.
|
|
754
|
+
var VERSION = "0.14.0";
|
|
709
755
|
|
|
710
756
|
// src/client.ts
|
|
711
757
|
var DEFAULT_BASE_URL = "https://api.sonilo.com";
|
|
@@ -736,6 +782,7 @@ var SoniloClient = class {
|
|
|
736
782
|
this.videoToVideoSfx = new VideoToVideoSfx(this);
|
|
737
783
|
this.videoToSound = new VideoToSound(this);
|
|
738
784
|
this.videoToVideoSound = new VideoToVideoSound(this);
|
|
785
|
+
this.audioDucking = new AudioDucking(this);
|
|
739
786
|
this.dubbing = new Dubbing(this);
|
|
740
787
|
}
|
|
741
788
|
/**
|