sonilo 0.17.0 → 0.18.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 +50 -0
- package/dist/index.cjs +34 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -3
- package/dist/index.d.ts +94 -3
- package/dist/index.js +34 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -397,6 +397,56 @@ The result is a `DubbingResult`, whose `outputs` is a map of language code to
|
|
|
397
397
|
dubbed `.mp4` URL — not the `audio`/`video`/`output_url` shape the other
|
|
398
398
|
endpoints use.
|
|
399
399
|
|
|
400
|
+
### Subtitle scripts
|
|
401
|
+
|
|
402
|
+
`subtitles` supplies the lines to speak, one script per target language:
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
const result = await client.dubbing.generate(
|
|
406
|
+
{
|
|
407
|
+
videoUrl: "https://example.com/clip.mp4",
|
|
408
|
+
languages: ["ja", "es"],
|
|
409
|
+
subtitles: { ja: "./ja.srt", es: "https://example.com/es.vtt" },
|
|
410
|
+
exportSrt: true,
|
|
411
|
+
},
|
|
412
|
+
{ timeout: 7_200_000 },
|
|
413
|
+
);
|
|
414
|
+
console.log(result.subtitles?.ja); // re-timed .srt URL
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Rules:
|
|
418
|
+
|
|
419
|
+
- These are **target-language** scripts, not source-language transcripts.
|
|
420
|
+
- The keys must match `languages` exactly — a missing or extra code is a 422,
|
|
421
|
+
checked server-side before anything is charged.
|
|
422
|
+
- A value starting with `https://` is a URL; any other string is a local file
|
|
423
|
+
path (Node.js only). A browser can pass a `File` instead. The filename must
|
|
424
|
+
end in `.srt` or `.vtt`, and an uploaded script is at most 1 MiB.
|
|
425
|
+
- `exportSrt` requires `subtitles`. With it, each language's delivered audio is
|
|
426
|
+
force-aligned against its script and a re-timed SRT is returned under
|
|
427
|
+
`subtitles`, keeping your lines verbatim.
|
|
428
|
+
|
|
429
|
+
`subtitle_preflight` (what the pipeline made of each script) and
|
|
430
|
+
`subtitle_export` (how each re-timed SRT came out) are maps keyed by language.
|
|
431
|
+
Their numeric fields — `cue_count`, `changes_count`, `alignment_loss` — are
|
|
432
|
+
typed `number | string`, because a finished task carries them as strings; read
|
|
433
|
+
them through `Number(...)`. `report_url` is `string | null` — the key is always
|
|
434
|
+
written, so test the value, not the key. An export whose `status` is `blocked`
|
|
435
|
+
does not fail the task: the dubbed videos are still delivered and `subtitles`
|
|
436
|
+
simply lacks that language.
|
|
437
|
+
|
|
438
|
+
`submit()` returns a `DubbingTask`: the shared `SfxTask` plus the
|
|
439
|
+
acknowledgement's own `subtitle_preflight`. A `review_required` status there
|
|
440
|
+
means the pipeline **changed lines in the script you submitted**, and
|
|
441
|
+
`changes_count` says how many — worth checking without waiting for the dub:
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
const task = await client.dubbing.submit({ videoUrl, languages, subtitles });
|
|
445
|
+
for (const [language, report] of Object.entries(task.subtitle_preflight ?? {})) {
|
|
446
|
+
if (report.status !== "ok") console.warn(language, report.status, report.issues);
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
400
450
|
## Video analysis
|
|
401
451
|
|
|
402
452
|
`client.videoAnalysis` analyzes a video and returns a **creative brief** for
|
package/dist/index.cjs
CHANGED
|
@@ -756,6 +756,17 @@ var AudioDucking = class {
|
|
|
756
756
|
};
|
|
757
757
|
|
|
758
758
|
// src/resources/dubbing.ts
|
|
759
|
+
var SUBTITLE_EXTENSIONS = [".srt", ".vtt"];
|
|
760
|
+
async function toSubtitleBlob(language, value) {
|
|
761
|
+
const name = typeof value === "string" ? value : value?.name ?? "";
|
|
762
|
+
const lowered = name.toLowerCase();
|
|
763
|
+
if (!SUBTITLE_EXTENSIONS.some((ext) => lowered.endsWith(ext))) {
|
|
764
|
+
throw new SoniloError(
|
|
765
|
+
`subtitles[${language}]: "${name}" must be an .srt or .vtt file, or an https:// URL`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
return toUploadBlob(value);
|
|
769
|
+
}
|
|
759
770
|
async function buildDubbingForm(params) {
|
|
760
771
|
if (params.video === void 0 === (params.videoUrl === void 0)) {
|
|
761
772
|
throw new SoniloError("Provide exactly one of video or videoUrl");
|
|
@@ -782,12 +793,34 @@ async function buildDubbingForm(params) {
|
|
|
782
793
|
if (params.lipsync !== void 0) {
|
|
783
794
|
form.set("lipsync", String(params.lipsync));
|
|
784
795
|
}
|
|
796
|
+
if (params.subtitles !== void 0) {
|
|
797
|
+
for (const [language, value] of Object.entries(params.subtitles)) {
|
|
798
|
+
const field = `subtitles[${language}]`;
|
|
799
|
+
if (typeof value === "string" && value.toLowerCase().startsWith("https://")) {
|
|
800
|
+
form.set(field, value);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
const { blob, filename } = await toSubtitleBlob(language, value);
|
|
804
|
+
form.set(field, blob, filename);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (params.exportSrt !== void 0) {
|
|
808
|
+
if (params.exportSrt && Object.keys(params.subtitles ?? {}).length === 0) {
|
|
809
|
+
throw new SoniloError("exportSrt requires subtitles \u2014 there is nothing to align against");
|
|
810
|
+
}
|
|
811
|
+
form.set("export_srt", String(params.exportSrt));
|
|
812
|
+
}
|
|
785
813
|
return form;
|
|
786
814
|
}
|
|
787
815
|
var Dubbing = class {
|
|
788
816
|
constructor(client) {
|
|
789
817
|
this.client = client;
|
|
790
818
|
}
|
|
819
|
+
/** The acknowledgement carries `subtitle_preflight` when scripts were sent,
|
|
820
|
+
* which is why this returns `DubbingTask` rather than the shared `SfxTask`:
|
|
821
|
+
* a `review_required` preflight means the pipeline altered lines in the
|
|
822
|
+
* script that was submitted, and typing it away hides that from every
|
|
823
|
+
* caller who only ever sees the 202. */
|
|
791
824
|
async submit(params) {
|
|
792
825
|
const res = await this.client.request("/v1/dubbing", {
|
|
793
826
|
method: "POST",
|
|
@@ -834,7 +867,7 @@ var VideoAnalysis = class {
|
|
|
834
867
|
};
|
|
835
868
|
|
|
836
869
|
// src/version.ts
|
|
837
|
-
var VERSION = "0.
|
|
870
|
+
var VERSION = "0.18.0";
|
|
838
871
|
|
|
839
872
|
// src/client.ts
|
|
840
873
|
var DEFAULT_BASE_URL = "https://api.sonilo.com";
|