realtime-avatar 0.27.1 → 0.27.3
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 +28 -2
- package/dist/{chunk-DOHZ365B.js → chunk-QBOIOZ3O.js} +1 -1
- package/dist/{chunk-4IEGR75H.js → chunk-W4DGBG4Z.js} +17 -6
- package/dist/express.d.ts +2 -2
- package/dist/express.js +2 -2
- package/dist/hono.d.ts +2 -2
- package/dist/hono.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/nextjs.d.ts +2 -2
- package/dist/nextjs.js +2 -2
- package/dist/{proxy-client-Cs2azqD1.d.ts → proxy-client-BAY77sJZ.d.ts} +38 -0
- package/dist/react-native.d.ts +2 -2
- package/dist/react-native.js +15 -5
- package/dist/react.d.ts +4 -4
- package/dist/react.js +76 -26
- package/dist/{recording-DjswTx5B.d.ts → recording-ydFVySSd.d.ts} +35 -0
- package/dist/recording.d.ts +9 -3
- package/dist/recording.js +23 -8
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tanstack-start.d.ts +2 -2
- package/dist/tanstack-start.js +2 -2
- package/dist/{types-6MUqx800.d.ts → types-kgmS7ECd.d.ts} +134 -8
- package/dist/{types-JtCwa0Ip.d.ts → types-lgS4Z9rd.d.ts} +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -2249,13 +2249,17 @@ var AVATAR_VIDEO_BADGE = {
|
|
|
2249
2249
|
var AVATAR_VIDEO_BADGE_DOT = { width: 6, height: 6, borderRadius: 9999, background: "#34d399" };
|
|
2250
2250
|
var AVATAR_VIDEO_OVERLAY = { position: "absolute", inset: 0, zIndex: 40 };
|
|
2251
2251
|
var RECORDED_MEDIA_MODES = ["audio", "video", "audio_video"];
|
|
2252
|
-
var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.");
|
|
2252
|
+
var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants", "tracks"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.");
|
|
2253
2253
|
var RECORDING_STATUSES = ["pending", "recording", "processing", "ready", "failed", "expired"];
|
|
2254
2254
|
z.enum(RECORDING_STATUSES);
|
|
2255
2255
|
var recordingParticipantSchema = z.object({
|
|
2256
2256
|
participantIdentity: z.string().min(1).max(160),
|
|
2257
2257
|
role: z.enum(["user", "avatar"])
|
|
2258
2258
|
}).strict();
|
|
2259
|
+
var recordingMutedIntervalSchema = z.object({
|
|
2260
|
+
startMs: z.number().int().min(0),
|
|
2261
|
+
endMs: z.number().int().positive()
|
|
2262
|
+
}).strict().refine((interval) => interval.endMs > interval.startMs, "Muted interval must end after it starts");
|
|
2259
2263
|
var recordingMetadataSchema = z.object({
|
|
2260
2264
|
sessionId: z.string().min(1),
|
|
2261
2265
|
recordingId: z.string().min(1),
|
|
@@ -2264,13 +2268,15 @@ var recordingMetadataSchema = z.object({
|
|
|
2264
2268
|
retainedUntil: z.string().datetime({ offset: true }),
|
|
2265
2269
|
participant: recordingParticipantSchema.optional(),
|
|
2266
2270
|
mediaStartedAt: z.string().datetime({ offset: true }).optional(),
|
|
2267
|
-
mediaEndedAt: z.string().datetime({ offset: true }).optional()
|
|
2271
|
+
mediaEndedAt: z.string().datetime({ offset: true }).optional(),
|
|
2272
|
+
mediaStartOffsetMs: z.number().int().min(0).optional().describe("First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero."),
|
|
2273
|
+
mediaMutedIntervalsMs: z.array(recordingMutedIntervalSchema).optional().describe("Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals.")
|
|
2268
2274
|
});
|
|
2269
2275
|
var recordingArtifactSchema = z.discriminatedUnion("status", [
|
|
2270
2276
|
recordingMetadataSchema.extend({ status: z.enum(["pending", "recording", "processing", "expired"]) }).strict(),
|
|
2271
2277
|
recordingMetadataSchema.extend({
|
|
2272
2278
|
status: z.literal("ready"),
|
|
2273
|
-
mediaType: z.enum(["audio/mp4", "video/mp4"]),
|
|
2279
|
+
mediaType: z.enum(["audio/mp4", "video/mp4", "audio/ogg", "video/webm"]),
|
|
2274
2280
|
sizeBytes: z.number().int().positive(),
|
|
2275
2281
|
durationMs: z.number().int().nonnegative().nullable()
|
|
2276
2282
|
}).strict(),
|
|
@@ -2333,6 +2339,8 @@ var transcriptionOptionsSchema = z.object({
|
|
|
2333
2339
|
languageCodes: transcriptionWireSchema.shape.language_codes,
|
|
2334
2340
|
customVocabulary: transcriptionWireSchema.shape.custom_vocabulary
|
|
2335
2341
|
}).strict();
|
|
2342
|
+
var schema02 = z.enum(["minor-sexual-content-v1"]);
|
|
2343
|
+
var contentPolicySchema = schema02;
|
|
2336
2344
|
|
|
2337
2345
|
// ../client/src/wire.ts
|
|
2338
2346
|
var DEFAULT_AVATAR_ID = "maria";
|
|
@@ -2499,7 +2507,8 @@ z.object({
|
|
|
2499
2507
|
support_edits: sessionSupportEditsSchema.optional(),
|
|
2500
2508
|
transcript_webhook: transcriptWebhookSchema.optional(),
|
|
2501
2509
|
client_metadata: clientMetadataSchema.optional(),
|
|
2502
|
-
connection_history: z.boolean().optional()
|
|
2510
|
+
connection_history: z.boolean().optional(),
|
|
2511
|
+
content_policy: contentPolicySchema.optional()
|
|
2503
2512
|
}).strict().superRefine((value, ctx) => {
|
|
2504
2513
|
if (value.support_edits && value.render_backend === "generative") {
|
|
2505
2514
|
ctx.addIssue({
|
|
@@ -2567,7 +2576,8 @@ z.object({
|
|
|
2567
2576
|
supportEdits: sessionSupportEditsSchema.optional(),
|
|
2568
2577
|
transcriptWebhook: transcriptWebhookSchema.optional(),
|
|
2569
2578
|
clientMetadata: clientMetadataSchema.optional(),
|
|
2570
|
-
connectionHistory: z.boolean().optional()
|
|
2579
|
+
connectionHistory: z.boolean().optional(),
|
|
2580
|
+
contentPolicy: contentPolicySchema.optional()
|
|
2571
2581
|
}).strict();
|
|
2572
2582
|
z.object({
|
|
2573
2583
|
recording: recordingArtifactSchema.optional(),
|
|
@@ -4588,43 +4598,57 @@ function recordingTimeline(recordings) {
|
|
|
4588
4598
|
if (new Set(recordings.map((recording) => recording.sessionId)).size > 1) throw new Error("Recordings belong to different sessions");
|
|
4589
4599
|
if (new Set(recordings.map((recording) => recording.recordingId)).size !== recordings.length) throw new Error("Duplicate recording assets");
|
|
4590
4600
|
const participants = recordings.filter((recording) => recording.participant && recording.status === "ready");
|
|
4591
|
-
if (new Set(participants.map((recording) => recording.participant?.role)).size !== participants.length) throw new Error("Duplicate participant recordings");
|
|
4592
4601
|
const entries = participants.map((recording) => {
|
|
4593
4602
|
if (recording.status !== "ready" || !recording.mediaStartedAt || !recording.mediaEndedAt || recording.durationMs === null) {
|
|
4594
4603
|
throw new Error("Recording media timing is unavailable");
|
|
4595
4604
|
}
|
|
4596
4605
|
const start = Date.parse(recording.mediaStartedAt);
|
|
4597
4606
|
const end = Math.min(Date.parse(recording.mediaEndedAt), start + recording.durationMs);
|
|
4598
|
-
|
|
4599
|
-
|
|
4607
|
+
const activeStart = start + (recording.mediaStartOffsetMs ?? 0);
|
|
4608
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || !Number.isFinite(activeStart) || end <= activeStart) throw new Error("Recording media timing is invalid");
|
|
4609
|
+
return { recording, start, activeStart, end };
|
|
4600
4610
|
});
|
|
4601
4611
|
if (!entries.length) return null;
|
|
4612
|
+
const laneEnds = /* @__PURE__ */ new Map();
|
|
4613
|
+
for (const { recording, activeStart, end } of [...entries].sort((a, b) => a.activeStart - b.activeStart)) {
|
|
4614
|
+
const kinds = recording.mode === "audio_video" ? ["audio", "video"] : [recording.mode];
|
|
4615
|
+
for (const kind of kinds) {
|
|
4616
|
+
const lane = `${recording.participant?.role}:${kind}`;
|
|
4617
|
+
if (activeStart < (laneEnds.get(lane) ?? -Infinity)) throw new Error(`Duplicate participant ${kind} recordings overlap`);
|
|
4618
|
+
laneEnds.set(lane, end);
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4602
4621
|
const originMs = Math.min(...entries.map((entry) => entry.start));
|
|
4603
4622
|
return {
|
|
4604
4623
|
originMs,
|
|
4605
4624
|
durationMs: Math.max(...entries.map((entry) => entry.end)) - originMs,
|
|
4606
|
-
entries: entries.map(({ recording, start, end }) => ({ recording, startMs: start - originMs, endMs: end - originMs }))
|
|
4625
|
+
entries: entries.map(({ recording, start, activeStart, end }) => ({ recording, startMs: start - originMs, activeStartMs: activeStart - originMs, endMs: end - originMs }))
|
|
4607
4626
|
};
|
|
4608
4627
|
}
|
|
4609
4628
|
function recordingPositions(timeline, positionMs) {
|
|
4610
4629
|
return timeline.entries.map((entry) => ({
|
|
4611
4630
|
recordingId: entry.recording.recordingId,
|
|
4612
|
-
active: positionMs >= entry.
|
|
4631
|
+
active: positionMs >= entry.activeStartMs && positionMs < entry.endMs,
|
|
4613
4632
|
timeSeconds: Math.max(0, Math.min(positionMs, entry.endMs) - entry.startMs) / 1e3
|
|
4614
4633
|
}));
|
|
4615
4634
|
}
|
|
4616
|
-
function
|
|
4617
|
-
|
|
4635
|
+
function recordingVideoVisible(recording, timeSeconds) {
|
|
4636
|
+
const mediaMs = timeSeconds * 1e3;
|
|
4637
|
+
return recording.mode !== "audio" && !(recording.mediaMutedIntervalsMs ?? []).some(
|
|
4638
|
+
(interval) => mediaMs >= interval.startMs && mediaMs < interval.endMs
|
|
4639
|
+
);
|
|
4618
4640
|
}
|
|
4619
4641
|
function RecordingPlayer({ assets, className }) {
|
|
4642
|
+
const timelineKey = JSON.stringify(assets.map((asset) => asset.recording));
|
|
4620
4643
|
const timeline = useMemo(() => {
|
|
4621
4644
|
try {
|
|
4622
4645
|
return recordingTimeline(assets.map((asset) => asset.recording));
|
|
4623
4646
|
} catch {
|
|
4624
4647
|
return null;
|
|
4625
4648
|
}
|
|
4626
|
-
}, [
|
|
4649
|
+
}, [timelineKey]);
|
|
4627
4650
|
const media = useRef(/* @__PURE__ */ new Map());
|
|
4651
|
+
const seeks = useRef(/* @__PURE__ */ new Map());
|
|
4628
4652
|
const position = useRef(0);
|
|
4629
4653
|
const anchor = useRef(null);
|
|
4630
4654
|
const [displayPosition, setDisplayPosition] = useState(0);
|
|
@@ -4632,6 +4656,11 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4632
4656
|
const [buffering, setBuffering] = useState(false);
|
|
4633
4657
|
const [error, setError] = useState("");
|
|
4634
4658
|
const sourceKey = assets.map((asset) => `${asset.recording.recordingId}:${asset.url}`).join("|");
|
|
4659
|
+
const seekMedia = (recordingId, element, seconds) => {
|
|
4660
|
+
if (element.readyState < 1 || Math.abs(element.currentTime - seconds) <= 0.02) return;
|
|
4661
|
+
seeks.current.set(recordingId, { seconds, retries: 0 });
|
|
4662
|
+
element.currentTime = seconds;
|
|
4663
|
+
};
|
|
4635
4664
|
useEffect(() => {
|
|
4636
4665
|
if (!timeline) return;
|
|
4637
4666
|
const sessionId = timeline.entries[0].recording.sessionId;
|
|
@@ -4648,6 +4677,7 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4648
4677
|
let last = performance.now();
|
|
4649
4678
|
let painted = last;
|
|
4650
4679
|
const starting = /* @__PURE__ */ new Set();
|
|
4680
|
+
const active = /* @__PURE__ */ new Set();
|
|
4651
4681
|
const stopMedia = () => {
|
|
4652
4682
|
for (const element of media.current.values()) element.pause();
|
|
4653
4683
|
};
|
|
@@ -4664,19 +4694,23 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4664
4694
|
const states = recordingPositions(timeline, proposed);
|
|
4665
4695
|
const waiting = playing && states.some((state) => {
|
|
4666
4696
|
const element = media.current.get(state.recordingId);
|
|
4667
|
-
return state.active && (!element || !
|
|
4697
|
+
return state.active && (!element || (!element.ended || seeks.current.has(state.recordingId)) && (element.readyState < 2 || element.seeking));
|
|
4668
4698
|
});
|
|
4669
4699
|
setBuffering(waiting);
|
|
4670
4700
|
if (!waiting) position.current = proposed;
|
|
4671
4701
|
for (const state of recordingPositions(timeline, position.current)) {
|
|
4672
4702
|
const element = media.current.get(state.recordingId);
|
|
4673
4703
|
if (!element) continue;
|
|
4674
|
-
if (!state.active
|
|
4704
|
+
if (!state.active) {
|
|
4675
4705
|
element.pause();
|
|
4706
|
+
active.delete(state.recordingId);
|
|
4676
4707
|
continue;
|
|
4677
4708
|
}
|
|
4678
|
-
if (
|
|
4679
|
-
|
|
4709
|
+
if (!active.has(state.recordingId)) seekMedia(state.recordingId, element, state.timeSeconds);
|
|
4710
|
+
if (element.readyState >= 1) active.add(state.recordingId);
|
|
4711
|
+
if (element.ended && !seeks.current.has(state.recordingId)) {
|
|
4712
|
+
element.pause();
|
|
4713
|
+
continue;
|
|
4680
4714
|
}
|
|
4681
4715
|
if (!playing || waiting) {
|
|
4682
4716
|
element.pause();
|
|
@@ -4712,14 +4746,14 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4712
4746
|
stopMedia();
|
|
4713
4747
|
};
|
|
4714
4748
|
}, [timeline, playing, sourceKey]);
|
|
4715
|
-
const
|
|
4749
|
+
const positions = new Map(timeline ? recordingPositions(timeline, displayPosition).map((state) => [state.recordingId, state]) : []);
|
|
4716
4750
|
const seek = (value) => {
|
|
4717
4751
|
position.current = value;
|
|
4718
4752
|
setDisplayPosition(value);
|
|
4719
4753
|
if (timeline) for (const state of recordingPositions(timeline, value)) {
|
|
4720
4754
|
const element = media.current.get(state.recordingId);
|
|
4721
4755
|
if (!element) continue;
|
|
4722
|
-
|
|
4756
|
+
seekMedia(state.recordingId, element, state.timeSeconds);
|
|
4723
4757
|
if (!state.active) element.pause();
|
|
4724
4758
|
}
|
|
4725
4759
|
};
|
|
@@ -4733,8 +4767,8 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4733
4767
|
if (position.current >= timeline.durationMs) seek(0);
|
|
4734
4768
|
for (const state of recordingPositions(timeline, position.current)) {
|
|
4735
4769
|
const element = media.current.get(state.recordingId);
|
|
4736
|
-
if (state.active && element
|
|
4737
|
-
|
|
4770
|
+
if (state.active && element) {
|
|
4771
|
+
seekMedia(state.recordingId, element, state.timeSeconds);
|
|
4738
4772
|
void element.play().catch((cause) => {
|
|
4739
4773
|
if (cause instanceof Error && cause.name === "AbortError") return;
|
|
4740
4774
|
for (const current of media.current.values()) current.pause();
|
|
@@ -4748,6 +4782,8 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4748
4782
|
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
4749
4783
|
/* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 240px), 1fr))", gap: 12 }, children: assets.map(({ recording, url }) => {
|
|
4750
4784
|
if (!recording.participant) return null;
|
|
4785
|
+
const state = positions.get(recording.recordingId);
|
|
4786
|
+
const visible = Boolean(state?.active && recordingVideoVisible(recording, state.timeSeconds));
|
|
4751
4787
|
const label = recording.participant.role === "user" ? "User" : "Character";
|
|
4752
4788
|
const ref = (element) => {
|
|
4753
4789
|
if (element) media.current.set(recording.recordingId, element);
|
|
@@ -4758,22 +4794,36 @@ function RecordingPlayer({ assets, className }) {
|
|
|
4758
4794
|
setPlaying(false);
|
|
4759
4795
|
setError("A recording could not load. Refresh access and try again.");
|
|
4760
4796
|
};
|
|
4761
|
-
|
|
4797
|
+
const onLoadedMetadata = (event) => {
|
|
4798
|
+
const state2 = timeline && recordingPositions(timeline, position.current).find((item) => item.recordingId === recording.recordingId);
|
|
4799
|
+
if (state2) seekMedia(recording.recordingId, event.currentTarget, state2.timeSeconds);
|
|
4800
|
+
};
|
|
4801
|
+
const onSeeked = (event) => {
|
|
4802
|
+
const pending = seeks.current.get(recording.recordingId);
|
|
4803
|
+
if (!pending) return;
|
|
4804
|
+
if (Math.abs(event.currentTarget.currentTime - pending.seconds) > 0.08 && pending.retries < 2) {
|
|
4805
|
+
pending.retries++;
|
|
4806
|
+
event.currentTarget.currentTime = pending.seconds;
|
|
4807
|
+
} else seeks.current.delete(recording.recordingId);
|
|
4808
|
+
};
|
|
4809
|
+
return recording.mode !== "audio" ? /* @__PURE__ */ jsxs("figure", { hidden: !visible, style: { margin: 0 }, children: [
|
|
4762
4810
|
/* @__PURE__ */ jsx("figcaption", { children: label }),
|
|
4763
4811
|
/* @__PURE__ */ jsx(
|
|
4764
4812
|
"video",
|
|
4765
4813
|
{
|
|
4766
4814
|
ref,
|
|
4767
4815
|
src: url,
|
|
4816
|
+
muted: recording.mode === "video",
|
|
4768
4817
|
playsInline: true,
|
|
4769
4818
|
preload: "auto",
|
|
4770
4819
|
onError,
|
|
4771
|
-
|
|
4820
|
+
onLoadedMetadata,
|
|
4821
|
+
onSeeked,
|
|
4822
|
+
hidden: !visible,
|
|
4772
4823
|
style: { width: "100%", maxHeight: 360, objectFit: "contain" }
|
|
4773
4824
|
}
|
|
4774
|
-
)
|
|
4775
|
-
|
|
4776
|
-
] }, recording.recordingId) : /* @__PURE__ */ jsx("audio", { ref, src: url, preload: "auto", onError, "aria-label": `${label} audio` }, recording.recordingId);
|
|
4825
|
+
)
|
|
4826
|
+
] }, recording.recordingId) : /* @__PURE__ */ jsx("audio", { ref, src: url, preload: "auto", onError, onLoadedMetadata, onSeeked, "aria-label": `${label} audio` }, recording.recordingId);
|
|
4777
4827
|
}) }),
|
|
4778
4828
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
4779
4829
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: toggle, disabled: !timeline, style: { minHeight: 44, minWidth: 44 }, children: playing ? "Pause" : "Play" }),
|
|
@@ -6,6 +6,7 @@ declare const recordingModeSchema: z.ZodEnum<{
|
|
|
6
6
|
audio_video: "audio_video";
|
|
7
7
|
off: "off";
|
|
8
8
|
participants: "participants";
|
|
9
|
+
tracks: "tracks";
|
|
9
10
|
}>;
|
|
10
11
|
type RecordingMode = z.infer<typeof recordingModeSchema>;
|
|
11
12
|
declare const recordingArtifactStatusSchema: z.ZodEnum<{
|
|
@@ -44,6 +45,11 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
44
45
|
}, z.core.$strict>>;
|
|
45
46
|
mediaStartedAt: z.ZodOptional<z.ZodString>;
|
|
46
47
|
mediaEndedAt: z.ZodOptional<z.ZodString>;
|
|
48
|
+
mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
50
|
+
startMs: z.ZodNumber;
|
|
51
|
+
endMs: z.ZodNumber;
|
|
52
|
+
}, z.core.$strict>>>;
|
|
47
53
|
status: z.ZodEnum<{
|
|
48
54
|
pending: "pending";
|
|
49
55
|
recording: "recording";
|
|
@@ -69,10 +75,17 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
69
75
|
}, z.core.$strict>>;
|
|
70
76
|
mediaStartedAt: z.ZodOptional<z.ZodString>;
|
|
71
77
|
mediaEndedAt: z.ZodOptional<z.ZodString>;
|
|
78
|
+
mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
|
|
79
|
+
mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
80
|
+
startMs: z.ZodNumber;
|
|
81
|
+
endMs: z.ZodNumber;
|
|
82
|
+
}, z.core.$strict>>>;
|
|
72
83
|
status: z.ZodLiteral<"ready">;
|
|
73
84
|
mediaType: z.ZodEnum<{
|
|
74
85
|
"audio/mp4": "audio/mp4";
|
|
75
86
|
"video/mp4": "video/mp4";
|
|
87
|
+
"audio/ogg": "audio/ogg";
|
|
88
|
+
"video/webm": "video/webm";
|
|
76
89
|
}>;
|
|
77
90
|
sizeBytes: z.ZodNumber;
|
|
78
91
|
durationMs: z.ZodNullable<z.ZodNumber>;
|
|
@@ -95,6 +108,11 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
95
108
|
}, z.core.$strict>>;
|
|
96
109
|
mediaStartedAt: z.ZodOptional<z.ZodString>;
|
|
97
110
|
mediaEndedAt: z.ZodOptional<z.ZodString>;
|
|
111
|
+
mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
|
|
112
|
+
mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
113
|
+
startMs: z.ZodNumber;
|
|
114
|
+
endMs: z.ZodNumber;
|
|
115
|
+
}, z.core.$strict>>>;
|
|
98
116
|
status: z.ZodLiteral<"failed">;
|
|
99
117
|
errorCode: z.ZodEnum<{
|
|
100
118
|
recording_failed: "recording_failed";
|
|
@@ -128,6 +146,11 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
|
|
|
128
146
|
}, z.core.$strict>>;
|
|
129
147
|
mediaStartedAt: z.ZodOptional<z.ZodString>;
|
|
130
148
|
mediaEndedAt: z.ZodOptional<z.ZodString>;
|
|
149
|
+
mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
|
|
150
|
+
mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
151
|
+
startMs: z.ZodNumber;
|
|
152
|
+
endMs: z.ZodNumber;
|
|
153
|
+
}, z.core.$strict>>>;
|
|
131
154
|
status: z.ZodEnum<{
|
|
132
155
|
pending: "pending";
|
|
133
156
|
recording: "recording";
|
|
@@ -153,10 +176,17 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
|
|
|
153
176
|
}, z.core.$strict>>;
|
|
154
177
|
mediaStartedAt: z.ZodOptional<z.ZodString>;
|
|
155
178
|
mediaEndedAt: z.ZodOptional<z.ZodString>;
|
|
179
|
+
mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
|
|
180
|
+
mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
181
|
+
startMs: z.ZodNumber;
|
|
182
|
+
endMs: z.ZodNumber;
|
|
183
|
+
}, z.core.$strict>>>;
|
|
156
184
|
status: z.ZodLiteral<"ready">;
|
|
157
185
|
mediaType: z.ZodEnum<{
|
|
158
186
|
"audio/mp4": "audio/mp4";
|
|
159
187
|
"video/mp4": "video/mp4";
|
|
188
|
+
"audio/ogg": "audio/ogg";
|
|
189
|
+
"video/webm": "video/webm";
|
|
160
190
|
}>;
|
|
161
191
|
sizeBytes: z.ZodNumber;
|
|
162
192
|
durationMs: z.ZodNullable<z.ZodNumber>;
|
|
@@ -179,6 +209,11 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
|
|
|
179
209
|
}, z.core.$strict>>;
|
|
180
210
|
mediaStartedAt: z.ZodOptional<z.ZodString>;
|
|
181
211
|
mediaEndedAt: z.ZodOptional<z.ZodString>;
|
|
212
|
+
mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
|
|
213
|
+
mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
214
|
+
startMs: z.ZodNumber;
|
|
215
|
+
endMs: z.ZodNumber;
|
|
216
|
+
}, z.core.$strict>>>;
|
|
182
217
|
status: z.ZodLiteral<"failed">;
|
|
183
218
|
errorCode: z.ZodEnum<{
|
|
184
219
|
recording_failed: "recording_failed";
|
package/dist/recording.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RecordingArtifact } from './recording-
|
|
2
|
-
export { L as ListRecordingsQuery, a as ListRecordingsResponse, b as RecordingAccessResponse, c as RecordingArtifactStatus, d as RecordingMode, e as RecordingParticipant, l as listRecordingsQuerySchema, f as listRecordingsResponseSchema, r as recordingAccessResponseSchema, g as recordingArtifactSchema, h as recordingArtifactStatusSchema, i as recordingModeSchema, j as recordingParticipantSchema } from './recording-
|
|
1
|
+
import { R as RecordingArtifact } from './recording-ydFVySSd.js';
|
|
2
|
+
export { L as ListRecordingsQuery, a as ListRecordingsResponse, b as RecordingAccessResponse, c as RecordingArtifactStatus, d as RecordingMode, e as RecordingParticipant, l as listRecordingsQuerySchema, f as listRecordingsResponseSchema, r as recordingAccessResponseSchema, g as recordingArtifactSchema, h as recordingArtifactStatusSchema, i as recordingModeSchema, j as recordingParticipantSchema } from './recording-ydFVySSd.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
declare function recordingTimeline(recordings: readonly RecordingArtifact[]): {
|
|
@@ -13,7 +13,7 @@ declare function recordingTimeline(recordings: readonly RecordingArtifact[]): {
|
|
|
13
13
|
createdAt: string;
|
|
14
14
|
retainedUntil: string;
|
|
15
15
|
status: "ready";
|
|
16
|
-
mediaType: "audio/mp4" | "video/mp4";
|
|
16
|
+
mediaType: "audio/mp4" | "video/mp4" | "audio/ogg" | "video/webm";
|
|
17
17
|
sizeBytes: number;
|
|
18
18
|
durationMs: number | null;
|
|
19
19
|
participant?: {
|
|
@@ -22,8 +22,14 @@ declare function recordingTimeline(recordings: readonly RecordingArtifact[]): {
|
|
|
22
22
|
} | undefined;
|
|
23
23
|
mediaStartedAt?: string | undefined;
|
|
24
24
|
mediaEndedAt?: string | undefined;
|
|
25
|
+
mediaStartOffsetMs?: number | undefined;
|
|
26
|
+
mediaMutedIntervalsMs?: {
|
|
27
|
+
startMs: number;
|
|
28
|
+
endMs: number;
|
|
29
|
+
}[] | undefined;
|
|
25
30
|
};
|
|
26
31
|
startMs: number;
|
|
32
|
+
activeStartMs: number;
|
|
27
33
|
endMs: number;
|
|
28
34
|
}[];
|
|
29
35
|
} | null;
|
package/dist/recording.js
CHANGED
|
@@ -2,13 +2,17 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
// ../http-client/src/generated/recording.ts
|
|
4
4
|
var RECORDED_MEDIA_MODES = ["audio", "video", "audio_video"];
|
|
5
|
-
var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.");
|
|
5
|
+
var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants", "tracks"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.");
|
|
6
6
|
var RECORDING_STATUSES = ["pending", "recording", "processing", "ready", "failed", "expired"];
|
|
7
7
|
var recordingArtifactStatusSchema = z.enum(RECORDING_STATUSES);
|
|
8
8
|
var recordingParticipantSchema = z.object({
|
|
9
9
|
participantIdentity: z.string().min(1).max(160),
|
|
10
10
|
role: z.enum(["user", "avatar"])
|
|
11
11
|
}).strict();
|
|
12
|
+
var recordingMutedIntervalSchema = z.object({
|
|
13
|
+
startMs: z.number().int().min(0),
|
|
14
|
+
endMs: z.number().int().positive()
|
|
15
|
+
}).strict().refine((interval) => interval.endMs > interval.startMs, "Muted interval must end after it starts");
|
|
12
16
|
var recordingMetadataSchema = z.object({
|
|
13
17
|
sessionId: z.string().min(1),
|
|
14
18
|
recordingId: z.string().min(1),
|
|
@@ -17,13 +21,15 @@ var recordingMetadataSchema = z.object({
|
|
|
17
21
|
retainedUntil: z.string().datetime({ offset: true }),
|
|
18
22
|
participant: recordingParticipantSchema.optional(),
|
|
19
23
|
mediaStartedAt: z.string().datetime({ offset: true }).optional(),
|
|
20
|
-
mediaEndedAt: z.string().datetime({ offset: true }).optional()
|
|
24
|
+
mediaEndedAt: z.string().datetime({ offset: true }).optional(),
|
|
25
|
+
mediaStartOffsetMs: z.number().int().min(0).optional().describe("First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero."),
|
|
26
|
+
mediaMutedIntervalsMs: z.array(recordingMutedIntervalSchema).optional().describe("Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals.")
|
|
21
27
|
});
|
|
22
28
|
var recordingArtifactSchema = z.discriminatedUnion("status", [
|
|
23
29
|
recordingMetadataSchema.extend({ status: z.enum(["pending", "recording", "processing", "expired"]) }).strict(),
|
|
24
30
|
recordingMetadataSchema.extend({
|
|
25
31
|
status: z.literal("ready"),
|
|
26
|
-
mediaType: z.enum(["audio/mp4", "video/mp4"]),
|
|
32
|
+
mediaType: z.enum(["audio/mp4", "video/mp4", "audio/ogg", "video/webm"]),
|
|
27
33
|
sizeBytes: z.number().int().positive(),
|
|
28
34
|
durationMs: z.number().int().nonnegative().nullable()
|
|
29
35
|
}).strict(),
|
|
@@ -52,28 +58,37 @@ function recordingTimeline(recordings) {
|
|
|
52
58
|
if (new Set(recordings.map((recording) => recording.sessionId)).size > 1) throw new Error("Recordings belong to different sessions");
|
|
53
59
|
if (new Set(recordings.map((recording) => recording.recordingId)).size !== recordings.length) throw new Error("Duplicate recording assets");
|
|
54
60
|
const participants = recordings.filter((recording) => recording.participant && recording.status === "ready");
|
|
55
|
-
if (new Set(participants.map((recording) => recording.participant?.role)).size !== participants.length) throw new Error("Duplicate participant recordings");
|
|
56
61
|
const entries = participants.map((recording) => {
|
|
57
62
|
if (recording.status !== "ready" || !recording.mediaStartedAt || !recording.mediaEndedAt || recording.durationMs === null) {
|
|
58
63
|
throw new Error("Recording media timing is unavailable");
|
|
59
64
|
}
|
|
60
65
|
const start = Date.parse(recording.mediaStartedAt);
|
|
61
66
|
const end = Math.min(Date.parse(recording.mediaEndedAt), start + recording.durationMs);
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
const activeStart = start + (recording.mediaStartOffsetMs ?? 0);
|
|
68
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || !Number.isFinite(activeStart) || end <= activeStart) throw new Error("Recording media timing is invalid");
|
|
69
|
+
return { recording, start, activeStart, end };
|
|
64
70
|
});
|
|
65
71
|
if (!entries.length) return null;
|
|
72
|
+
const laneEnds = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const { recording, activeStart, end } of [...entries].sort((a, b) => a.activeStart - b.activeStart)) {
|
|
74
|
+
const kinds = recording.mode === "audio_video" ? ["audio", "video"] : [recording.mode];
|
|
75
|
+
for (const kind of kinds) {
|
|
76
|
+
const lane = `${recording.participant?.role}:${kind}`;
|
|
77
|
+
if (activeStart < (laneEnds.get(lane) ?? -Infinity)) throw new Error(`Duplicate participant ${kind} recordings overlap`);
|
|
78
|
+
laneEnds.set(lane, end);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
66
81
|
const originMs = Math.min(...entries.map((entry) => entry.start));
|
|
67
82
|
return {
|
|
68
83
|
originMs,
|
|
69
84
|
durationMs: Math.max(...entries.map((entry) => entry.end)) - originMs,
|
|
70
|
-
entries: entries.map(({ recording, start, end }) => ({ recording, startMs: start - originMs, endMs: end - originMs }))
|
|
85
|
+
entries: entries.map(({ recording, start, activeStart, end }) => ({ recording, startMs: start - originMs, activeStartMs: activeStart - originMs, endMs: end - originMs }))
|
|
71
86
|
};
|
|
72
87
|
}
|
|
73
88
|
function recordingPositions(timeline, positionMs) {
|
|
74
89
|
return timeline.entries.map((entry) => ({
|
|
75
90
|
recordingId: entry.recording.recordingId,
|
|
76
|
-
active: positionMs >= entry.
|
|
91
|
+
active: positionMs >= entry.activeStartMs && positionMs < entry.endMs,
|
|
77
92
|
timeSeconds: Math.max(0, Math.min(positionMs, entry.endMs) - entry.startMs) / 1e3
|
|
78
93
|
}));
|
|
79
94
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, RealtimeAvatarOptions, StartCallOptions, clipLibraryDeclarationSchema, verifyTranscript } from './index.js';
|
|
2
|
-
export { h as Asset, g as AssetKind, A as Avatar, o as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, p as CallConnection, C as CallMode, a as CallPolicy, q as CallQueued, r as ClipDeclaration, f as ClipLibrary, d as ClipLibraryDeclaration, s as ClipLibraryPlan, e as ClipLibraryUpdate, t as ClipSource, u as ContextMessage, n as CreditBalance, E as EndCallOptions, v as EndCallReason, w as ExternalLlm, k as ListRecordingsQuery, l as ListRecordingsResponse, i as ListSessionsOptions, L as LoopRedirect, m as RecordingAccessResponse, R as RecordingArtifact, x as RecordingArtifactStatus, y as RecordingMode, S as StartCallResult, T as TranscriptPayload, j as UsageSession, U as UsageSessionPage, V as VideoPolicy, z as isQueued, B as listRecordingsResponseSchema, D as recordingAccessResponseSchema, F as recordingArtifactSchema, G as recordingModeSchema } from './types-
|
|
2
|
+
export { h as Asset, g as AssetKind, A as Avatar, o as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, p as CallConnection, C as CallMode, a as CallPolicy, q as CallQueued, r as ClipDeclaration, f as ClipLibrary, d as ClipLibraryDeclaration, s as ClipLibraryPlan, e as ClipLibraryUpdate, t as ClipSource, u as ContextMessage, n as CreditBalance, E as EndCallOptions, v as EndCallReason, w as ExternalLlm, k as ListRecordingsQuery, l as ListRecordingsResponse, i as ListSessionsOptions, L as LoopRedirect, m as RecordingAccessResponse, R as RecordingArtifact, x as RecordingArtifactStatus, y as RecordingMode, S as StartCallResult, T as TranscriptPayload, j as UsageSession, U as UsageSessionPage, V as VideoPolicy, z as isQueued, B as listRecordingsResponseSchema, D as recordingAccessResponseSchema, F as recordingArtifactSchema, G as recordingModeSchema } from './types-kgmS7ECd.js';
|
|
3
3
|
import './connection-history.js';
|
|
4
4
|
import 'zod';
|
package/dist/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-W4DGBG4Z.js';
|
|
2
2
|
import './chunk-MCIXU6XP.js';
|
package/dist/tanstack-start.d.ts
CHANGED
package/dist/tanstack-start.js
CHANGED