mirror of
https://github.com/nestriness/nestri.git
synced 2026-08-01 17:34:15 +03:00
feat: Add types and IDE comments to session
This commit is contained in:
@@ -1,189 +1,38 @@
|
|||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
import { Identifier } from "../id.js";
|
import { fn } from "../utils/fn.js";
|
||||||
import { Examples } from "../examples.js";
|
import { sessionTable } from "./session.sql.js";
|
||||||
|
import {
|
||||||
|
MachinePresetName,
|
||||||
|
ScreenResolutionPresetName,
|
||||||
|
VideoCodecName,
|
||||||
|
AudioCodecName,
|
||||||
|
RetryOptions,
|
||||||
|
SessionInfo,
|
||||||
|
type SessionInfo as SessionInfoType,
|
||||||
|
type Session as SessionInterface,
|
||||||
|
type Config as SessionConfig,
|
||||||
|
} from "./types.js";
|
||||||
|
|
||||||
// Basic types we need
|
// Re-export all types and schemas from types.ts
|
||||||
export const MachinePresetName = z.enum([
|
export {
|
||||||
"ns3-micro",
|
MachinePresetName,
|
||||||
"ns3-mini",
|
ScreenResolutionPresetName,
|
||||||
"ns3-small",
|
VideoCodecName,
|
||||||
"ns3-medium",
|
AudioCodecName,
|
||||||
"ns3-large",
|
RetryOptions,
|
||||||
]);
|
SessionInfo,
|
||||||
|
};
|
||||||
|
|
||||||
export const ScreenResolutionPresetName = z.enum([
|
export type {
|
||||||
"1280x720",
|
SessionInfoType,
|
||||||
"1080x1920",
|
SessionInterface as Session,
|
||||||
"1440x2560",
|
SessionConfig as SessionConfigType,
|
||||||
"2160x3840",
|
};
|
||||||
]);
|
|
||||||
|
|
||||||
export const VideoCodecName = z.enum(["h264", "h265", "vp8", "vp9", "av1"]);
|
|
||||||
|
|
||||||
export const AudioCodecName = z.enum(["opus", "aac", "mp3", "pcm"]);
|
|
||||||
|
|
||||||
export const RetryOptions = z.object({
|
|
||||||
maxAttempts: z.number().int().optional(),
|
|
||||||
factor: z.number().optional(),
|
|
||||||
minTimeoutInMs: z.number().int().optional(),
|
|
||||||
maxTimeoutInMs: z.number().int().optional(),
|
|
||||||
randomize: z.boolean().optional(),
|
|
||||||
outOfMemory: z
|
|
||||||
.object({
|
|
||||||
machine: MachinePresetName.optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Maintain backward compatibility with existing namespace structure
|
||||||
export namespace Session {
|
export namespace Session {
|
||||||
export const Info = z
|
export const Info = SessionInfo;
|
||||||
.object({
|
export type Info = SessionInfoType;
|
||||||
id: Identifier.schema("session").meta({
|
|
||||||
description: "The unique identifier of the session",
|
|
||||||
example: Examples.Session.id,
|
|
||||||
}),
|
|
||||||
description: z.string().optional().meta({
|
|
||||||
description: "Description of what the session does",
|
|
||||||
example: Examples.Session.description,
|
|
||||||
}),
|
|
||||||
retry: RetryOptions.optional().meta({
|
|
||||||
description: "Retry configuration for failed session attempts",
|
|
||||||
example: Examples.Session.retry,
|
|
||||||
}),
|
|
||||||
queue: z
|
|
||||||
.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
concurrencyLimit: z.number().optional(),
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.meta({
|
|
||||||
description: "Queue configuration for concurrent session execution",
|
|
||||||
example: Examples.Session.queue,
|
|
||||||
}),
|
|
||||||
machine: MachinePresetName.optional().meta({
|
|
||||||
description: "Machine preset for the session",
|
|
||||||
example: Examples.Session.machine,
|
|
||||||
}),
|
|
||||||
maxDuration: z.number().optional().meta({
|
|
||||||
description: "Maximum duration in seconds the session can run",
|
|
||||||
example: Examples.Session.maxDuration,
|
|
||||||
}),
|
|
||||||
relayUrl: z.url().optional().meta({
|
|
||||||
description: "Nestri relay URL for streaming and signaling",
|
|
||||||
example: Examples.Session.relayUrl,
|
|
||||||
}),
|
|
||||||
resolution: ScreenResolutionPresetName.optional().meta({
|
|
||||||
description: "Display or stream resolution in 'WxH' format",
|
|
||||||
example: Examples.Session.resolution,
|
|
||||||
}),
|
|
||||||
framerate: z.number().optional().meta({
|
|
||||||
description: "Display or stream framerate",
|
|
||||||
example: Examples.Session.framerate,
|
|
||||||
}),
|
|
||||||
room: z.string().optional().meta({
|
|
||||||
description: "Room name or identifier for multiplayer sessions",
|
|
||||||
example: Examples.Session.room,
|
|
||||||
}),
|
|
||||||
gpuVendor: z.string().optional().meta({
|
|
||||||
description: "Preferred GPU vendor",
|
|
||||||
example: Examples.Session.gpuVendor,
|
|
||||||
}),
|
|
||||||
gpuName: z.string().optional().meta({
|
|
||||||
description: "Preferred GPU name",
|
|
||||||
example: Examples.Session.gpuName,
|
|
||||||
}),
|
|
||||||
gpuIndex: z.number().optional().meta({
|
|
||||||
description: "Specific GPU index to use",
|
|
||||||
example: Examples.Session.gpuIndex,
|
|
||||||
}),
|
|
||||||
gpuCardPath: z.string().optional().meta({
|
|
||||||
description: "Force specific GPU card by /dev/dri path",
|
|
||||||
example: Examples.Session.gpuCardPath,
|
|
||||||
}),
|
|
||||||
videoCodec: VideoCodecName.optional().meta({
|
|
||||||
description: "Preferred video codec for streaming",
|
|
||||||
example: Examples.Session.videoCodec,
|
|
||||||
}),
|
|
||||||
videoEncoder: z.string().optional().meta({
|
|
||||||
description: "Override video encoder implementation",
|
|
||||||
example: Examples.Session.videoEncoder,
|
|
||||||
}),
|
|
||||||
videoRateControl: z
|
|
||||||
.enum(["cbr", "vbr", "cq", "lossless"])
|
|
||||||
.optional()
|
|
||||||
.meta({
|
|
||||||
description: "Video rate control method",
|
|
||||||
example: Examples.Session.videoRateControl,
|
|
||||||
}),
|
|
||||||
videoCqp: z.number().min(1).max(51).optional().meta({
|
|
||||||
description: "Constant Quantization Parameter for quality control",
|
|
||||||
example: Examples.Session.videoCqp,
|
|
||||||
}),
|
|
||||||
videoBitrate: z.number().min(1).optional().meta({
|
|
||||||
description: "Target video bitrate in kbps",
|
|
||||||
example: Examples.Session.videoBitrate,
|
|
||||||
}),
|
|
||||||
videoBitrateMax: z.number().min(1).optional().meta({
|
|
||||||
description: "Maximum video bitrate in kbps",
|
|
||||||
example: Examples.Session.videoBitrateMax,
|
|
||||||
}),
|
|
||||||
videoEncoderType: z.enum(["hardware", "software"]).optional().meta({
|
|
||||||
description: "Encoder type: hardware or software",
|
|
||||||
example: Examples.Session.videoEncoderType,
|
|
||||||
}),
|
|
||||||
videoBitDepth: z
|
|
||||||
.union([z.literal(8), z.literal(10)])
|
|
||||||
.optional()
|
|
||||||
.meta({
|
|
||||||
description: "Video bit depth (8 or 10)",
|
|
||||||
example: Examples.Session.videoBitDepth,
|
|
||||||
}),
|
|
||||||
audioCaptureMethod: z
|
|
||||||
.enum(["pipewire", "pulse", "alsa"])
|
|
||||||
.optional()
|
|
||||||
.meta({
|
|
||||||
description: "Audio capture method",
|
|
||||||
example: Examples.Session.audioCaptureMethod,
|
|
||||||
}),
|
|
||||||
audioCodec: AudioCodecName.optional().meta({
|
|
||||||
description: "Preferred audio codec for streaming",
|
|
||||||
example: Examples.Session.audioCodec,
|
|
||||||
}),
|
|
||||||
audioEncoder: z.string().optional().meta({
|
|
||||||
description: "Override audio encoder binary",
|
|
||||||
example: Examples.Session.audioEncoder,
|
|
||||||
}),
|
|
||||||
audioRateControl: z.enum(["cbr", "vbr"]).optional().meta({
|
|
||||||
description: "Audio rate control method",
|
|
||||||
example: Examples.Session.audioRateControl,
|
|
||||||
}),
|
|
||||||
audioBitrate: z.number().min(1).optional().meta({
|
|
||||||
description: "Target audio bitrate in kbps",
|
|
||||||
example: Examples.Session.audioBitrate,
|
|
||||||
}),
|
|
||||||
audioBitrateMax: z.number().optional().meta({
|
|
||||||
description: "Maximum audio bitrate in kbps",
|
|
||||||
example: Examples.Session.audioBitrateMax,
|
|
||||||
}),
|
|
||||||
zeroCopy: z.boolean().optional().meta({
|
|
||||||
description: "Whether to use DMA-BUF for zero-copy video pipelines",
|
|
||||||
example: Examples.Session.zeroCopy,
|
|
||||||
}),
|
|
||||||
verbose: z.boolean().optional().meta({
|
|
||||||
description: "Enable verbose output for debugging",
|
|
||||||
example: Examples.Session.verbose,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.meta({
|
|
||||||
ref: "Session",
|
|
||||||
description:
|
|
||||||
"Represents a session configuration with streaming and execution options",
|
|
||||||
example: Examples.Session,
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Info = z.infer<typeof Info>;
|
|
||||||
|
|
||||||
// Session interface that matches the SDK session object
|
|
||||||
export interface Session {
|
export interface Session {
|
||||||
/** Optional description of the session */
|
/** Optional description of the session */
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -195,9 +44,49 @@ export namespace Session {
|
|||||||
startAndWait: (options?: any) => any;
|
startAndWait: (options?: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session configuration type that includes the run function
|
export type Config = Omit<SessionInfoType, "id"> & {
|
||||||
export type Config = Omit<Info, "id"> & {
|
|
||||||
/** The function that executes when the session is started */
|
/** The function that executes when the session is started */
|
||||||
run: (ctx: any) => Promise<any>;
|
run: (ctx: any) => Promise<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const list = fn(z.void(), () => {
|
||||||
|
// Placeholder implementation
|
||||||
|
});
|
||||||
|
|
||||||
|
export function serialize(
|
||||||
|
input: typeof sessionTable.$inferSelect,
|
||||||
|
): z.infer<typeof SessionInfo> {
|
||||||
|
return {
|
||||||
|
id: input.id,
|
||||||
|
room: input.room || undefined,
|
||||||
|
retry: input.retry || undefined,
|
||||||
|
queue: input.queue || undefined,
|
||||||
|
machine: input.machine || undefined,
|
||||||
|
description: input.description || undefined,
|
||||||
|
maxDuration: input.maxDuration || undefined,
|
||||||
|
relayUrl: input.relayUrl || undefined,
|
||||||
|
resolution: input.resolution || undefined,
|
||||||
|
framerate: input.framerate || undefined,
|
||||||
|
gpuVendor: input.gpuVendor || undefined,
|
||||||
|
gpuName: input.gpuName || undefined,
|
||||||
|
gpuIndex: input.gpuIndex || undefined,
|
||||||
|
gpuCardPath: input.gpuCardPath || undefined,
|
||||||
|
videoCodec: input.videoCodec || undefined,
|
||||||
|
videoEncoder: input.videoEncoder || undefined,
|
||||||
|
videoRateControl: input.videoRateControl || undefined,
|
||||||
|
videoCqp: input.videoCqp || undefined,
|
||||||
|
videoBitrate: input.videoBitrate || undefined,
|
||||||
|
videoBitrateMax: input.videoBitrateMax || undefined,
|
||||||
|
videoEncoderType: input.videoEncoderType || undefined,
|
||||||
|
videoBitDepth: input.videoBitDepth || undefined,
|
||||||
|
audioCaptureMethod: input.audioCaptureMethod || undefined,
|
||||||
|
audioCodec: input.audioCodec || undefined,
|
||||||
|
audioEncoder: input.audioEncoder || undefined,
|
||||||
|
audioRateControl: input.audioRateControl || undefined,
|
||||||
|
audioBitrate: input.audioBitrate || undefined,
|
||||||
|
audioBitrateMax: input.audioBitrateMax || undefined,
|
||||||
|
zeroCopy: input.zeroCopy || undefined,
|
||||||
|
verbose: input.verbose || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
packages/core/src/session/session.sql.ts
Normal file
66
packages/core/src/session/session.sql.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { id, timestamps } from "../drizzle/types.js";
|
||||||
|
import {
|
||||||
|
pgTable,
|
||||||
|
varchar,
|
||||||
|
text,
|
||||||
|
integer,
|
||||||
|
boolean,
|
||||||
|
jsonb,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import {
|
||||||
|
type Queue,
|
||||||
|
type RetryOptions,
|
||||||
|
type VideoCodecName,
|
||||||
|
type AudioCodecName,
|
||||||
|
type MachinePresetName,
|
||||||
|
type ScreenResolutionPresetName,
|
||||||
|
VideoEncoderType,
|
||||||
|
VideoRateControl,
|
||||||
|
AudioCaptureMethod,
|
||||||
|
AudioRateControl,
|
||||||
|
VideoBitDepth,
|
||||||
|
} from "./types.js";
|
||||||
|
|
||||||
|
export const sessionTable = pgTable("sessions", {
|
||||||
|
...id,
|
||||||
|
...timestamps,
|
||||||
|
description: text("description"),
|
||||||
|
retry: jsonb("retry").$type<RetryOptions>(),
|
||||||
|
queue: jsonb("queue").$type<Queue>(),
|
||||||
|
machine: varchar("machine", { length: 200 }).$type<MachinePresetName>(),
|
||||||
|
maxDuration: integer("max_duration"),
|
||||||
|
relayUrl: varchar("relay_url", { length: 500 }),
|
||||||
|
resolution: varchar("resolution", {
|
||||||
|
length: 20,
|
||||||
|
}).$type<ScreenResolutionPresetName>(),
|
||||||
|
framerate: integer("framerate"),
|
||||||
|
room: varchar("room", { length: 255 }),
|
||||||
|
gpuVendor: varchar("gpu_vendor", { length: 100 }),
|
||||||
|
gpuName: varchar("gpu_name", { length: 255 }),
|
||||||
|
gpuIndex: integer("gpu_index"),
|
||||||
|
gpuCardPath: varchar("gpu_card_path", { length: 500 }),
|
||||||
|
videoCodec: varchar("video_codec", { length: 20 }).$type<VideoCodecName>(),
|
||||||
|
videoEncoder: varchar("video_encoder", { length: 100 }),
|
||||||
|
videoRateControl: varchar("video_rate_control", {
|
||||||
|
length: 20,
|
||||||
|
}).$type<VideoRateControl>(),
|
||||||
|
videoCqp: integer("video_cqp"),
|
||||||
|
videoBitrate: integer("video_bitrate"),
|
||||||
|
videoBitrateMax: integer("video_bitrate_max"),
|
||||||
|
videoEncoderType: varchar("video_encoder_type", {
|
||||||
|
length: 20,
|
||||||
|
}).$type<VideoEncoderType>(),
|
||||||
|
videoBitDepth: integer("video_bit_depth").$type<VideoBitDepth>(),
|
||||||
|
audioCaptureMethod: varchar("audio_capture_method", {
|
||||||
|
length: 20,
|
||||||
|
}).$type<AudioCaptureMethod>(),
|
||||||
|
audioCodec: varchar("audio_codec", { length: 20 }).$type<AudioCodecName>(),
|
||||||
|
audioEncoder: varchar("audio_encoder", { length: 100 }),
|
||||||
|
audioRateControl: varchar("audio_rate_control", {
|
||||||
|
length: 20,
|
||||||
|
}).$type<AudioRateControl>(),
|
||||||
|
audioBitrate: integer("audio_bitrate"),
|
||||||
|
audioBitrateMax: integer("audio_bitrate_max"),
|
||||||
|
zeroCopy: boolean("zero_copy"),
|
||||||
|
verbose: boolean("verbose"),
|
||||||
|
});
|
||||||
286
packages/core/src/session/types.ts
Normal file
286
packages/core/src/session/types.ts
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
import { Identifier } from "../id.js";
|
||||||
|
import { Examples } from "../examples.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available machine preset sizes for session execution
|
||||||
|
*/
|
||||||
|
export const MachinePresetName = z.enum([
|
||||||
|
"ns3-micro",
|
||||||
|
"ns3-mini",
|
||||||
|
"ns3-small",
|
||||||
|
"ns3-medium",
|
||||||
|
"ns3-large",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type MachinePresetName = z.infer<typeof MachinePresetName>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available screen resolution presets in 'WxH' format
|
||||||
|
*/
|
||||||
|
export const ScreenResolutionPresetName = z.enum([
|
||||||
|
"1280x720",
|
||||||
|
"1080x1920",
|
||||||
|
"1440x2560",
|
||||||
|
"2160x3840",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type ScreenResolutionPresetName = z.infer<
|
||||||
|
typeof ScreenResolutionPresetName
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported video codecs for streaming
|
||||||
|
*/
|
||||||
|
export const VideoCodecName = z.enum(["h264", "h265", "vp8", "vp9", "av1"]);
|
||||||
|
|
||||||
|
export type VideoCodecName = z.infer<typeof VideoCodecName>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported audio codecs for streaming
|
||||||
|
*/
|
||||||
|
export const AudioCodecName = z.enum(["opus", "aac", "mp3", "pcm"]);
|
||||||
|
|
||||||
|
export type AudioCodecName = z.infer<typeof AudioCodecName>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry configuration for failed operations
|
||||||
|
*/
|
||||||
|
export const RetryOptions = z.object({
|
||||||
|
/** Maximum number of retry attempts */
|
||||||
|
maxAttempts: z.number().int().optional(),
|
||||||
|
/** Exponential backoff factor for retry delays */
|
||||||
|
factor: z.number().optional(),
|
||||||
|
/** Minimum timeout between retries in milliseconds */
|
||||||
|
minTimeoutInMs: z.number().int().optional(),
|
||||||
|
/** Maximum timeout between retries in milliseconds */
|
||||||
|
maxTimeoutInMs: z.number().int().optional(),
|
||||||
|
/** Whether to randomize retry delays to avoid thundering herd */
|
||||||
|
randomize: z.boolean().optional(),
|
||||||
|
/** Configuration for handling out of memory errors */
|
||||||
|
outOfMemory: z
|
||||||
|
.object({
|
||||||
|
/** Machine preset to upgrade to on out of memory errors */
|
||||||
|
machine: MachinePresetName.optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RetryOptions = z.infer<typeof RetryOptions>;
|
||||||
|
|
||||||
|
export const Queue = z.object({
|
||||||
|
/** Queue name for organizing sessions */
|
||||||
|
name: z.string().optional(),
|
||||||
|
/** Maximum number of concurrent sessions in queue */
|
||||||
|
concurrencyLimit: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Queue = z.infer<typeof Queue>;
|
||||||
|
|
||||||
|
export const VideoRateControl = z.enum(["cbr", "vbr", "cq", "lossless"]);
|
||||||
|
|
||||||
|
export type VideoRateControl = z.infer<typeof VideoRateControl>;
|
||||||
|
|
||||||
|
export const VideoEncoderType = z.enum(["hardware", "software"]);
|
||||||
|
|
||||||
|
export type VideoEncoderType = z.infer<typeof VideoEncoderType>;
|
||||||
|
|
||||||
|
export const AudioCaptureMethod = z.enum(["pipewire", "pulse", "alsa"]);
|
||||||
|
|
||||||
|
export type AudioCaptureMethod = z.infer<typeof AudioCaptureMethod>;
|
||||||
|
|
||||||
|
export const AudioRateControl = z.enum(["cbr", "vbr"]);
|
||||||
|
|
||||||
|
export type AudioRateControl = z.infer<typeof AudioRateControl>;
|
||||||
|
|
||||||
|
export const VideoBitDepth = z.union([z.literal(8), z.literal(10)]);
|
||||||
|
|
||||||
|
export type VideoBitDepth = z.infer<typeof VideoBitDepth>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a session configuration with streaming and execution options
|
||||||
|
*/
|
||||||
|
export const SessionInfo = z
|
||||||
|
.object({
|
||||||
|
/** The unique identifier of the session */
|
||||||
|
id: Identifier.schema("session").meta({
|
||||||
|
description: "The unique identifier of the session",
|
||||||
|
example: Examples.Session.id,
|
||||||
|
}),
|
||||||
|
/** Description of what the session does */
|
||||||
|
description: z.string().optional().meta({
|
||||||
|
description: "Description of what the session does",
|
||||||
|
example: Examples.Session.description,
|
||||||
|
}),
|
||||||
|
/** Retry configuration for failed session attempts */
|
||||||
|
retry: RetryOptions.optional().meta({
|
||||||
|
description: "Retry configuration for failed session attempts",
|
||||||
|
example: Examples.Session.retry,
|
||||||
|
}),
|
||||||
|
/** Queue configuration for concurrent session execution */
|
||||||
|
queue: Queue.optional().meta({
|
||||||
|
description: "Queue configuration for concurrent session execution",
|
||||||
|
example: Examples.Session.queue,
|
||||||
|
}),
|
||||||
|
/** Machine preset for the session */
|
||||||
|
machine: MachinePresetName.optional().meta({
|
||||||
|
description: "Machine preset for the session",
|
||||||
|
example: Examples.Session.machine,
|
||||||
|
}),
|
||||||
|
/** Maximum duration in seconds the session can run */
|
||||||
|
maxDuration: z.number().optional().meta({
|
||||||
|
description: "Maximum duration in seconds the session can run",
|
||||||
|
example: Examples.Session.maxDuration,
|
||||||
|
}),
|
||||||
|
/** Nestri relay URL for streaming and signaling */
|
||||||
|
relayUrl: z.url().optional().meta({
|
||||||
|
description: "Nestri relay URL for streaming and signaling",
|
||||||
|
example: Examples.Session.relayUrl,
|
||||||
|
}),
|
||||||
|
/** Display or stream resolution in 'WxH' format */
|
||||||
|
resolution: ScreenResolutionPresetName.optional().meta({
|
||||||
|
description: "Display or stream resolution in 'WxH' format",
|
||||||
|
example: Examples.Session.resolution,
|
||||||
|
}),
|
||||||
|
/** Display or stream framerate */
|
||||||
|
framerate: z.number().optional().meta({
|
||||||
|
description: "Display or stream framerate",
|
||||||
|
example: Examples.Session.framerate,
|
||||||
|
}),
|
||||||
|
/** Room name or identifier for multiplayer sessions */
|
||||||
|
room: z.string().optional().meta({
|
||||||
|
description: "Room name or identifier for multiplayer sessions",
|
||||||
|
example: Examples.Session.room,
|
||||||
|
}),
|
||||||
|
/** Preferred GPU vendor */
|
||||||
|
gpuVendor: z.string().optional().meta({
|
||||||
|
description: "Preferred GPU vendor",
|
||||||
|
example: Examples.Session.gpuVendor,
|
||||||
|
}),
|
||||||
|
/** Preferred GPU name */
|
||||||
|
gpuName: z.string().optional().meta({
|
||||||
|
description: "Preferred GPU name",
|
||||||
|
example: Examples.Session.gpuName,
|
||||||
|
}),
|
||||||
|
/** Specific GPU index to use */
|
||||||
|
gpuIndex: z.number().optional().meta({
|
||||||
|
description: "Specific GPU index to use",
|
||||||
|
example: Examples.Session.gpuIndex,
|
||||||
|
}),
|
||||||
|
/** Force specific GPU card by /dev/dri path */
|
||||||
|
gpuCardPath: z.string().optional().meta({
|
||||||
|
description: "Force specific GPU card by /dev/dri path",
|
||||||
|
example: Examples.Session.gpuCardPath,
|
||||||
|
}),
|
||||||
|
/** Preferred video codec for streaming */
|
||||||
|
videoCodec: VideoCodecName.optional().meta({
|
||||||
|
description: "Preferred video codec for streaming",
|
||||||
|
example: Examples.Session.videoCodec,
|
||||||
|
}),
|
||||||
|
/** Override video encoder implementation */
|
||||||
|
videoEncoder: z.string().optional().meta({
|
||||||
|
description: "Override video encoder implementation",
|
||||||
|
example: Examples.Session.videoEncoder,
|
||||||
|
}),
|
||||||
|
/** Video rate control method */
|
||||||
|
videoRateControl: VideoRateControl.optional().meta({
|
||||||
|
description: "Video rate control method",
|
||||||
|
example: Examples.Session.videoRateControl,
|
||||||
|
}),
|
||||||
|
/** Constant Quantization Parameter for quality control */
|
||||||
|
videoCqp: z.number().min(1).max(51).optional().meta({
|
||||||
|
description: "Constant Quantization Parameter for quality control",
|
||||||
|
example: Examples.Session.videoCqp,
|
||||||
|
}),
|
||||||
|
/** Target video bitrate in kbps */
|
||||||
|
videoBitrate: z.number().min(1).optional().meta({
|
||||||
|
description: "Target video bitrate in kbps",
|
||||||
|
example: Examples.Session.videoBitrate,
|
||||||
|
}),
|
||||||
|
/** Maximum video bitrate in kbps */
|
||||||
|
videoBitrateMax: z.number().min(1).optional().meta({
|
||||||
|
description: "Maximum video bitrate in kbps",
|
||||||
|
example: Examples.Session.videoBitrateMax,
|
||||||
|
}),
|
||||||
|
/** Encoder type: hardware or software */
|
||||||
|
videoEncoderType: VideoEncoderType.optional().meta({
|
||||||
|
description: "Encoder type: hardware or software",
|
||||||
|
example: Examples.Session.videoEncoderType,
|
||||||
|
}),
|
||||||
|
/** Video bit depth (8 or 10) */
|
||||||
|
videoBitDepth: VideoBitDepth.optional().meta({
|
||||||
|
description: "Video bit depth (8 or 10)",
|
||||||
|
example: Examples.Session.videoBitDepth,
|
||||||
|
}),
|
||||||
|
/** Audio capture method */
|
||||||
|
audioCaptureMethod: AudioCaptureMethod.optional().meta({
|
||||||
|
description: "Audio capture method",
|
||||||
|
example: Examples.Session.audioCaptureMethod,
|
||||||
|
}),
|
||||||
|
/** Preferred audio codec for streaming */
|
||||||
|
audioCodec: AudioCodecName.optional().meta({
|
||||||
|
description: "Preferred audio codec for streaming",
|
||||||
|
example: Examples.Session.audioCodec,
|
||||||
|
}),
|
||||||
|
/** Override audio encoder binary */
|
||||||
|
audioEncoder: z.string().optional().meta({
|
||||||
|
description: "Override audio encoder binary",
|
||||||
|
example: Examples.Session.audioEncoder,
|
||||||
|
}),
|
||||||
|
/** Audio rate control method */
|
||||||
|
audioRateControl: AudioRateControl.optional().meta({
|
||||||
|
description: "Audio rate control method",
|
||||||
|
example: Examples.Session.audioRateControl,
|
||||||
|
}),
|
||||||
|
/** Target audio bitrate in kbps */
|
||||||
|
audioBitrate: z.number().min(1).optional().meta({
|
||||||
|
description: "Target audio bitrate in kbps",
|
||||||
|
example: Examples.Session.audioBitrate,
|
||||||
|
}),
|
||||||
|
/** Maximum audio bitrate in kbps */
|
||||||
|
audioBitrateMax: z.number().optional().meta({
|
||||||
|
description: "Maximum audio bitrate in kbps",
|
||||||
|
example: Examples.Session.audioBitrateMax,
|
||||||
|
}),
|
||||||
|
/** Whether to use DMA-BUF for zero-copy video pipelines */
|
||||||
|
zeroCopy: z.boolean().optional().meta({
|
||||||
|
description: "Whether to use DMA-BUF for zero-copy video pipelines",
|
||||||
|
example: Examples.Session.zeroCopy,
|
||||||
|
}),
|
||||||
|
/** Enable verbose output for debugging */
|
||||||
|
verbose: z.boolean().optional().meta({
|
||||||
|
description: "Enable verbose output for debugging",
|
||||||
|
example: Examples.Session.verbose,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.meta({
|
||||||
|
ref: "Session",
|
||||||
|
description:
|
||||||
|
"Represents a session configuration with streaming and execution options",
|
||||||
|
example: Examples.Session,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Inferred type from SessionInfo schema */
|
||||||
|
export type SessionInfo = z.infer<typeof SessionInfo>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session interface that matches the SDK session object
|
||||||
|
*/
|
||||||
|
export interface Session {
|
||||||
|
/** Optional description of the session */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** Start a session without waiting for the result */
|
||||||
|
start: (options?: any, requestOptions?: any) => Promise<any>;
|
||||||
|
|
||||||
|
/** Start a session and wait for the result */
|
||||||
|
startAndWait: (options?: any) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session configuration type that includes the run function
|
||||||
|
*/
|
||||||
|
export type Config = Omit<SessionInfo, "id"> & {
|
||||||
|
/** The function that executes when the session is started */
|
||||||
|
run: (ctx: any) => Promise<any>;
|
||||||
|
};
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { describeRoute, resolver } from "hono-openapi";
|
import { notPublic } from "../utils/auth.js";
|
||||||
|
import { ErrorResponses } from "../utils/error.js";
|
||||||
import { Session } from "@nestri/core/session/index";
|
import { Session } from "@nestri/core/session/index";
|
||||||
|
import { describeRoute, resolver } from "hono-openapi";
|
||||||
|
|
||||||
export namespace SessionRoute {
|
export namespace SessionRoute {
|
||||||
export const route = new Hono().get(
|
export const route = new Hono().use(notPublic).get(
|
||||||
"/",
|
"/",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
tags: ["Session"],
|
tags: ["Session"],
|
||||||
@@ -19,7 +21,14 @@ export namespace SessionRoute {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
401: ErrorResponses[401],
|
||||||
|
403: ErrorResponses[403],
|
||||||
|
429: ErrorResponses[429],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const sessions = await Session.list();
|
||||||
|
return c.json(sessions);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,17 @@ const client = createClient({
|
|||||||
subjects,
|
subjects,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const notPublic: MiddlewareHandler = async (c, next) => {
|
||||||
|
const actor = Actor.use();
|
||||||
|
if (actor.type === "public")
|
||||||
|
throw new VisibleError(
|
||||||
|
"authentication",
|
||||||
|
ErrorCodes.Authentication.UNAUTHORIZED,
|
||||||
|
"Missing authorization header",
|
||||||
|
);
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
export const auth: MiddlewareHandler = async (c, next) => {
|
export const auth: MiddlewareHandler = async (c, next) => {
|
||||||
const authHeader =
|
const authHeader =
|
||||||
c.req.query("authorization") ?? c.req.header("authorization");
|
c.req.query("authorization") ?? c.req.header("authorization");
|
||||||
|
|||||||
138
packages/function/src/api/utils/error.ts
Normal file
138
packages/function/src/api/utils/error.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import { resolver } from "hono-openapi";
|
||||||
|
import { ErrorResponse } from "@nestri/core/error";
|
||||||
|
|
||||||
|
export const ErrorResponses = {
|
||||||
|
/**Validation error*/
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Validation error",
|
||||||
|
example: {
|
||||||
|
type: "validation",
|
||||||
|
code: "invalid_parameter",
|
||||||
|
message: "The request was invalid",
|
||||||
|
param: "email",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Bad Request - The request could not be understood or was missing required parameters.",
|
||||||
|
},
|
||||||
|
/**Authentication Error*/
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Authentication error",
|
||||||
|
example: {
|
||||||
|
type: "authentication",
|
||||||
|
code: "unauthorized",
|
||||||
|
message: "Authentication required",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Unauthorized - Authentication is required and has failed or has not been provided.",
|
||||||
|
},
|
||||||
|
/**Permission Error*/
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Permission error",
|
||||||
|
example: {
|
||||||
|
type: "forbidden",
|
||||||
|
code: "permission_denied",
|
||||||
|
message: "You do not have permission to access this resource",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Forbidden - You do not have permission to access this resource.",
|
||||||
|
},
|
||||||
|
/**Not Found Error*/
|
||||||
|
404: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Not found error",
|
||||||
|
example: {
|
||||||
|
type: "not_found",
|
||||||
|
code: "resource_not_found",
|
||||||
|
message: "The requested resource could not be found",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Not Found - The requested resource does not exist.",
|
||||||
|
},
|
||||||
|
/**Conflict Error*/
|
||||||
|
409: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Conflict Error",
|
||||||
|
example: {
|
||||||
|
type: "already_exists",
|
||||||
|
code: "resource_already_exists",
|
||||||
|
message:
|
||||||
|
"The resource could not be created because it already exists",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Conflict - The resource could not be created because it already exists.",
|
||||||
|
},
|
||||||
|
/**Rate Limit Error*/
|
||||||
|
429: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Rate limit error",
|
||||||
|
example: {
|
||||||
|
type: "rate_limit",
|
||||||
|
code: "too_many_requests",
|
||||||
|
message: "Rate limit exceeded",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Too Many Requests - You have made too many requests in a short period of time.",
|
||||||
|
},
|
||||||
|
/**Internal Server Error*/
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
ErrorResponse.meta({
|
||||||
|
description: "Server error",
|
||||||
|
example: {
|
||||||
|
type: "internal",
|
||||||
|
code: "internal_error",
|
||||||
|
message: "Internal server error",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Internal Server Error - Something went wrong on our end.",
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user