vargai 0.4.0-alpha111 → 0.4.0-alpha112
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/package.json +1 -1
- package/src/react/resolve.ts +36 -120
- package/src/react/types.ts +43 -0
package/package.json
CHANGED
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"license": "Apache-2.0",
|
|
108
108
|
"author": "varg.ai <hello@varg.ai> (https://varg.ai)",
|
|
109
109
|
"sideEffects": false,
|
|
110
|
-
"version": "0.4.0-
|
|
110
|
+
"version": "0.4.0-alpha112",
|
|
111
111
|
"exports": {
|
|
112
112
|
".": "./src/index.ts",
|
|
113
113
|
"./ai": "./src/ai-sdk/index.ts",
|
package/src/react/resolve.ts
CHANGED
|
@@ -904,100 +904,34 @@ async function resolveSourceUrl(
|
|
|
904
904
|
throw new Error("cannot resolve source URL from input");
|
|
905
905
|
}
|
|
906
906
|
|
|
907
|
-
/** Get the varg gateway client settings. */
|
|
908
|
-
function getGatewayConfig(): { apiKey: string; baseUrl: string } | null {
|
|
909
|
-
const apiKey = process.env.VARG_API_KEY;
|
|
910
|
-
if (!apiKey) return null;
|
|
911
|
-
return {
|
|
912
|
-
apiKey,
|
|
913
|
-
baseUrl: process.env.VARG_API_URL ?? "https://api.varg.ai",
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
/** Call gateway and wait for job completion. */
|
|
918
|
-
async function gatewayJobRequest(
|
|
919
|
-
path: string,
|
|
920
|
-
body: Record<string, unknown>,
|
|
921
|
-
): Promise<Record<string, unknown>> {
|
|
922
|
-
const config = getGatewayConfig();
|
|
923
|
-
if (!config) throw new Error("VARG_API_KEY not set");
|
|
924
|
-
|
|
925
|
-
const submitRes = await fetch(`${config.baseUrl}/v1${path}`, {
|
|
926
|
-
method: "POST",
|
|
927
|
-
headers: {
|
|
928
|
-
"Content-Type": "application/json",
|
|
929
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
930
|
-
},
|
|
931
|
-
body: JSON.stringify(body),
|
|
932
|
-
signal: AbortSignal.timeout(30_000),
|
|
933
|
-
});
|
|
934
|
-
if (!submitRes.ok) {
|
|
935
|
-
const text = await submitRes.text().catch(() => "");
|
|
936
|
-
throw new Error(`gateway ${path} failed: ${submitRes.status} ${text}`);
|
|
937
|
-
}
|
|
938
|
-
const job = (await submitRes.json()) as {
|
|
939
|
-
job_id: string;
|
|
940
|
-
status: string;
|
|
941
|
-
output?: Record<string, unknown>;
|
|
942
|
-
};
|
|
943
|
-
|
|
944
|
-
if (job.status === "completed" && job.output) return job;
|
|
945
|
-
|
|
946
|
-
const maxAttempts = 300;
|
|
947
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
948
|
-
await new Promise((r) => setTimeout(r, 2_000));
|
|
949
|
-
const pollRes = await fetch(`${config.baseUrl}/v1/jobs/${job.job_id}`, {
|
|
950
|
-
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
951
|
-
signal: AbortSignal.timeout(15_000),
|
|
952
|
-
});
|
|
953
|
-
if (!pollRes.ok) continue;
|
|
954
|
-
const result = (await pollRes.json()) as {
|
|
955
|
-
status: string;
|
|
956
|
-
output?: Record<string, unknown>;
|
|
957
|
-
error?: string;
|
|
958
|
-
};
|
|
959
|
-
if (result.status === "completed") return result;
|
|
960
|
-
if (result.status === "failed")
|
|
961
|
-
throw new Error(`gateway job failed: ${result.error ?? "unknown"}`);
|
|
962
|
-
}
|
|
963
|
-
throw new Error("gateway job timed out");
|
|
964
|
-
}
|
|
965
|
-
|
|
966
907
|
/**
|
|
967
|
-
* Resolve a Slice element -- splits video into segments.
|
|
968
|
-
*
|
|
908
|
+
* Resolve a Slice element -- splits video into segments via gateway.
|
|
909
|
+
* Requires `gateway` prop (e.g., varg) for authenticated API access.
|
|
969
910
|
*/
|
|
970
911
|
export async function resolveSliceElement(
|
|
971
912
|
element: VargElement<"slice">,
|
|
972
913
|
props: import("./types").SliceProps,
|
|
973
914
|
): Promise<ResolvedElement<"slice">> {
|
|
915
|
+
if (!props.gateway) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
"await Slice() requires 'gateway' prop (e.g., Slice({ gateway: varg, src: video, every: 5 }))",
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
|
|
974
921
|
const srcUrl = await resolveSourceUrl(props.src);
|
|
975
922
|
|
|
976
|
-
const
|
|
923
|
+
const result = await props.gateway.slice({
|
|
977
924
|
video_url: srcUrl,
|
|
978
925
|
codec: props.codec ?? "copy",
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const result = await gatewayJobRequest("/ffmpeg/slice", body);
|
|
986
|
-
const output = result.output as
|
|
987
|
-
| { url?: string; metadata?: Record<string, unknown> }
|
|
988
|
-
| undefined;
|
|
989
|
-
const metadata = output?.metadata as
|
|
990
|
-
| {
|
|
991
|
-
segments?: Array<{ url: string; index: number; filename: string }>;
|
|
992
|
-
total_segments?: number;
|
|
993
|
-
}
|
|
994
|
-
| undefined;
|
|
995
|
-
|
|
996
|
-
const segmentData = metadata?.segments ?? [];
|
|
926
|
+
every: props.every,
|
|
927
|
+
at: props.at,
|
|
928
|
+
count: props.count,
|
|
929
|
+
ranges: props.ranges,
|
|
930
|
+
});
|
|
997
931
|
|
|
998
932
|
const segments: import("./types").SliceSegment[] = [];
|
|
999
933
|
let cursor = 0;
|
|
1000
|
-
for (const seg of
|
|
934
|
+
for (const seg of result.segments) {
|
|
1001
935
|
const segFile = File.fromUrl(seg.url);
|
|
1002
936
|
const segDuration = await probeDuration(segFile);
|
|
1003
937
|
const segElement = new ResolvedElement(
|
|
@@ -1015,8 +949,8 @@ export async function resolveSliceElement(
|
|
|
1015
949
|
cursor += segDuration;
|
|
1016
950
|
}
|
|
1017
951
|
|
|
1018
|
-
const firstFile =
|
|
1019
|
-
? File.fromUrl(
|
|
952
|
+
const firstFile = result.segments[0]
|
|
953
|
+
? File.fromUrl(result.segments[0].url)
|
|
1020
954
|
: File.fromBuffer(new Uint8Array(0), "video/mp4");
|
|
1021
955
|
|
|
1022
956
|
return new ResolvedElement(element, {
|
|
@@ -1028,11 +962,18 @@ export async function resolveSliceElement(
|
|
|
1028
962
|
|
|
1029
963
|
/**
|
|
1030
964
|
* Resolve an FFmpeg element -- runs arbitrary FFmpeg command via gateway.
|
|
965
|
+
* Requires `gateway` prop (e.g., varg) for authenticated API access.
|
|
1031
966
|
*/
|
|
1032
967
|
export async function resolveFFmpegElement(
|
|
1033
968
|
element: VargElement<"ffmpeg">,
|
|
1034
969
|
props: import("./types").FFmpegProps,
|
|
1035
970
|
): Promise<ResolvedElement<"ffmpeg">> {
|
|
971
|
+
if (!props.gateway) {
|
|
972
|
+
throw new Error(
|
|
973
|
+
"await FFmpeg() requires 'gateway' prop (e.g., FFmpeg({ gateway: varg, command: '...' }))",
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
|
|
1036
977
|
const inputFiles: Record<string, string> = {};
|
|
1037
978
|
if (props.src) {
|
|
1038
979
|
inputFiles.in_1 = await resolveSourceUrl(props.src);
|
|
@@ -1049,64 +990,39 @@ export async function resolveFFmpegElement(
|
|
|
1049
990
|
}
|
|
1050
991
|
|
|
1051
992
|
const outputFiles = command.includes("OUTPUT_FOLDER")
|
|
1052
|
-
? "OUTPUT_FOLDER"
|
|
993
|
+
? ("OUTPUT_FOLDER" as const)
|
|
1053
994
|
: { out_1: "output.mp4" };
|
|
1054
995
|
|
|
1055
|
-
const result = await
|
|
996
|
+
const result = await props.gateway.ffmpeg({
|
|
1056
997
|
command,
|
|
1057
998
|
input_files: inputFiles,
|
|
1058
999
|
output_files: outputFiles,
|
|
1059
1000
|
});
|
|
1060
1001
|
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
| undefined;
|
|
1064
|
-
const url = output?.url ?? "";
|
|
1065
|
-
const file = url
|
|
1066
|
-
? File.fromUrl(url)
|
|
1002
|
+
const file = result.url
|
|
1003
|
+
? File.fromUrl(result.url)
|
|
1067
1004
|
: File.fromBuffer(new Uint8Array(0), "video/mp4");
|
|
1068
|
-
const duration = url ? await probeDuration(file) : 0;
|
|
1005
|
+
const duration = result.url ? await probeDuration(file) : 0;
|
|
1069
1006
|
|
|
1070
1007
|
return new ResolvedElement(element, { file, duration });
|
|
1071
1008
|
}
|
|
1072
1009
|
|
|
1073
1010
|
/**
|
|
1074
1011
|
* Resolve a Probe element -- gets media metadata via gateway.
|
|
1012
|
+
* Requires `gateway` prop (e.g., varg) for authenticated API access.
|
|
1075
1013
|
*/
|
|
1076
1014
|
export async function resolveProbeElement(
|
|
1077
1015
|
element: VargElement<"probe">,
|
|
1078
1016
|
props: import("./types").ProbeProps,
|
|
1079
1017
|
): Promise<ResolvedElement<"probe">> {
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const res = await fetch(`${config.baseUrl}/v1/ffmpeg/probe`, {
|
|
1086
|
-
method: "POST",
|
|
1087
|
-
headers: {
|
|
1088
|
-
"Content-Type": "application/json",
|
|
1089
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
1090
|
-
},
|
|
1091
|
-
body: JSON.stringify({ url: srcUrl }),
|
|
1092
|
-
signal: AbortSignal.timeout(30_000),
|
|
1093
|
-
});
|
|
1094
|
-
if (!res.ok) {
|
|
1095
|
-
const text = await res.text().catch(() => "");
|
|
1096
|
-
throw new Error(`probe failed: ${res.status} ${text}`);
|
|
1018
|
+
if (!props.gateway) {
|
|
1019
|
+
throw new Error(
|
|
1020
|
+
"await Probe() requires 'gateway' prop (e.g., Probe({ gateway: varg, src: video }))",
|
|
1021
|
+
);
|
|
1097
1022
|
}
|
|
1098
1023
|
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
width?: number;
|
|
1102
|
-
height?: number;
|
|
1103
|
-
codec?: string;
|
|
1104
|
-
audio_codec?: string;
|
|
1105
|
-
format?: string;
|
|
1106
|
-
bitrate?: number;
|
|
1107
|
-
fps?: number;
|
|
1108
|
-
size_bytes?: number;
|
|
1109
|
-
};
|
|
1024
|
+
const srcUrl = await resolveSourceUrl(props.src);
|
|
1025
|
+
const probeData = await props.gateway.probe({ url: srcUrl });
|
|
1110
1026
|
|
|
1111
1027
|
const file = File.fromUrl(srcUrl);
|
|
1112
1028
|
|
package/src/react/types.ts
CHANGED
|
@@ -406,6 +406,43 @@ export interface RenderResult {
|
|
|
406
406
|
|
|
407
407
|
// ── FFmpeg processing element props ──────────────────────────────────
|
|
408
408
|
|
|
409
|
+
/** Minimal gateway interface for FFmpeg utility methods. Implemented by VargProvider. */
|
|
410
|
+
export interface FFmpegGateway {
|
|
411
|
+
slice(params: {
|
|
412
|
+
video_url: string;
|
|
413
|
+
codec?: "copy" | "reencode";
|
|
414
|
+
every?: number;
|
|
415
|
+
at?: number[];
|
|
416
|
+
count?: number;
|
|
417
|
+
ranges?: Array<{ start: number; end: number }>;
|
|
418
|
+
}): Promise<{
|
|
419
|
+
url: string;
|
|
420
|
+
segments: Array<{ url: string; index: number; filename: string }>;
|
|
421
|
+
jobId: string;
|
|
422
|
+
}>;
|
|
423
|
+
probe(params: { url: string }): Promise<{
|
|
424
|
+
duration?: number;
|
|
425
|
+
width?: number;
|
|
426
|
+
height?: number;
|
|
427
|
+
codec?: string;
|
|
428
|
+
audio_codec?: string;
|
|
429
|
+
format?: string;
|
|
430
|
+
bitrate?: number;
|
|
431
|
+
fps?: number;
|
|
432
|
+
size_bytes?: number;
|
|
433
|
+
}>;
|
|
434
|
+
ffmpeg(params: {
|
|
435
|
+
command: string;
|
|
436
|
+
input_files?: Record<string, string>;
|
|
437
|
+
output_files?: Record<string, string> | string;
|
|
438
|
+
}): Promise<{
|
|
439
|
+
url: string;
|
|
440
|
+
mediaType: string;
|
|
441
|
+
jobId: string;
|
|
442
|
+
metadata?: Record<string, unknown>;
|
|
443
|
+
}>;
|
|
444
|
+
}
|
|
445
|
+
|
|
409
446
|
export interface SliceProps {
|
|
410
447
|
/** Source video: URL string, File object, or ResolvedElement */
|
|
411
448
|
src: string | File | VargElement;
|
|
@@ -419,6 +456,8 @@ export interface SliceProps {
|
|
|
419
456
|
count?: number;
|
|
420
457
|
/** Split at explicit time ranges */
|
|
421
458
|
ranges?: Array<{ start: number; end: number }>;
|
|
459
|
+
/** Authenticated gateway provider (e.g., varg). Required for cloud rendering. */
|
|
460
|
+
gateway?: FFmpegGateway;
|
|
422
461
|
}
|
|
423
462
|
|
|
424
463
|
export interface FFmpegProps {
|
|
@@ -428,11 +467,15 @@ export interface FFmpegProps {
|
|
|
428
467
|
inputs?: Record<string, string | File | VargElement>;
|
|
429
468
|
/** FFmpeg command flags (without -i input, which is added automatically for src) */
|
|
430
469
|
command: string;
|
|
470
|
+
/** Authenticated gateway provider (e.g., varg). Required for cloud rendering. */
|
|
471
|
+
gateway?: FFmpegGateway;
|
|
431
472
|
}
|
|
432
473
|
|
|
433
474
|
export interface ProbeProps {
|
|
434
475
|
/** Source to probe: URL string, File object, or ResolvedElement */
|
|
435
476
|
src: string | File | VargElement;
|
|
477
|
+
/** Authenticated gateway provider (e.g., varg). Required for cloud rendering. */
|
|
478
|
+
gateway?: FFmpegGateway;
|
|
436
479
|
}
|
|
437
480
|
|
|
438
481
|
/**
|