import { homedir } from "node:os"; import path from "node:path"; import type { MediaInferenceProvider, MediaSourceProvider } from "@rakazo/adapter-kit"; import { expandHome } from "@rakazo/media/node"; import { EmulatorMediaInferenceProvider } from "./media-emulator.js"; import { EmulatorMediaSourceProvider } from "./media-emulator-source.js"; import { FolderMediaSourceProvider } from "./media-folder-source.js"; import { SidecarMediaInferenceProvider } from "./media-sidecar.js"; export interface MediaConfig { /** Media Studio runs: a sidecar is configured or the scripted runtime uses the emulator. */ enabled: boolean; workerUrl: string | null; workerToken: string | null; profilesDir: string; /** `${DATA_DIR}/media`, absolute. Every derived file and sidecar path lives here. */ mediaRoot: string; } export type MediaInferenceKind = "sidecar" | "emulator"; /** Emulators replace the sidecar whenever the scripted runtime is pinned (tests, e2e). */ export function mediaEmulatorEnabled(env: NodeJS.ProcessEnv = process.env): boolean { return env.AGENT_RUNTIME === "scripted"; } export function mediaConfigFromEnv(env: NodeJS.ProcessEnv = process.env): MediaConfig { const workerUrl = env.MEDIA_WORKER_URL?.trim() || null; const workerToken = env.MEDIA_WORKER_TOKEN?.trim() || null; return { enabled: mediaEmulatorEnabled(env) || Boolean(workerUrl && workerToken), workerUrl, workerToken, profilesDir: path.resolve( expandHome(env.MEDIA_PROFILES_DIR?.trim() || "~/.evoo/media-profiles", homedir()), ), mediaRoot: path.resolve(env.DATA_DIR ?? "./data", "media"), }; } export function resolveMediaInferenceKind( env: NodeJS.ProcessEnv = process.env, ): MediaInferenceKind | null { if (mediaEmulatorEnabled(env)) return "emulator"; const config = mediaConfigFromEnv(env); return config.workerUrl && config.workerToken ? "sidecar" : null; } /** The configured inference provider, or null when Media Studio is not set up. */ export function createMediaInferenceProvider( env: NodeJS.ProcessEnv = process.env, ): MediaInferenceProvider | null { const config = mediaConfigFromEnv(env); switch (resolveMediaInferenceKind(env)) { case "emulator": return new EmulatorMediaInferenceProvider({ root: config.mediaRoot }); case "sidecar": return new SidecarMediaInferenceProvider({ baseUrl: config.workerUrl as string, token: config.workerToken as string, }); default: return null; } } /** Emulator clips are cached inside the media root so inference can read them. */ export function emulatorFixturesDir(env: NodeJS.ProcessEnv = process.env): string { return path.join(mediaConfigFromEnv(env).mediaRoot, "emulator-library"); } /** * Server-side source adapter for a persisted MediaSource kind. Client-pushed * kinds (apple_photos, ios_library) have no server adapter. */ export function createMediaSourceProvider( kind: string, env: NodeJS.ProcessEnv = process.env, ): MediaSourceProvider { switch (kind) { case "folder": return new FolderMediaSourceProvider(); case "emulator": if (!mediaEmulatorEnabled(env)) break; return new EmulatorMediaSourceProvider({ fixturesDir: emulatorFixturesDir(env) }); default: break; } throw new Error(`No server-side media source for "${kind}". Use folder.`); }