Create standalone browser port of LTX Athena with authenticated native API transport

This commit is contained in:
Mikei386 committed 2026-09-29 17:59:23 +02:00
commit 1333b0c36b
202 files changed
+63054

No files matched your search

@@ -0,0 +1,129 @@
import { addVisualAssetToProject } from './asset-copy'
import { toModelsDirRelativeRef } from './lora-library'
import { logger } from './logger'
import type { RecoveryGenType, RecoveryImporter } from './generation-recovery'
import { videoGenerationModeFromInputs } from './multi-keyframe'
// Mirrors GenSpace's own live-generation completion effect (see the `videoPath` effect in
// GenSpace.tsx) but operates on the recovery marker's captured context instead of live component
// state, so it can run in the background while that project's GenSpace isn't mounted. ic-lora/
// retake/extend write a marker with no full `settings` (see GenerationRecoveryContext) and
// recover as a standalone video asset here too; retake/extend additionally set `ctx.model` so
// the pipeline label isn't forced to 'fast'.
const importVideo: RecoveryImporter = async (ctx, result, { addAsset, modelsDir }) => {
const videoPath = typeof result === 'string' ? result : result[0]
if (!videoPath) return
const copied = await addVisualAssetToProject(videoPath, ctx.projectId, 'video')
if (!copied) throw new Error('Could not persist generated video to project storage')
const s = ctx.settings
const genMode = videoGenerationModeFromInputs({
keyframes: ctx.keyframes,
audioUrl: ctx.inputAudioUrl,
imageUrl: ctx.inputImageUrl,
})
addAsset(ctx.projectId, {
type: 'video',
path: copied.path,
bigThumbnailPath: copied.bigThumbnailPath,
smallThumbnailPath: copied.smallThumbnailPath,
width: copied.width,
height: copied.height,
prompt: ctx.prompt,
resolution: s?.videoResolution ?? '',
duration: s?.duration ?? undefined,
generationParams: {
mode: genMode,
prompt: ctx.prompt,
model: ctx.model ?? s?.model ?? 'fast',
modelLabel: ctx.modelLabel,
duration: s?.duration ?? null,
resolution: s?.videoResolution ?? '',
fps: s?.fps ?? 24,
audio: s?.audio ?? false,
cameraMotion: 'none',
imageAspectRatio: s?.aspectRatio,
imageSteps: 4,
inputImageUrl: ctx.inputImageUrl,
inputLastImageUrl: ctx.inputLastImageUrl,
inputAudioUrl: ctx.inputAudioUrl,
keyframes: ctx.keyframes && ctx.keyframes.length > 0 ? ctx.keyframes : undefined,
loras: s?.loras && s.loras.length > 0
? s.loras.map(l => ({ ref: toModelsDirRelativeRef(l.ref, modelsDir), name: l.name, scale: l.scale }))
: undefined,
},
takes: [{
path: copied.path,
bigThumbnailPath: copied.bigThumbnailPath,
smallThumbnailPath: copied.smallThumbnailPath,
width: copied.width,
height: copied.height,
createdAt: Date.now(),
}],
activeTakeIndex: 0,
})
}
// Mirrors GenSpace's `imagePaths` completion effect. A failed copy for one image in a multi-image
// batch is logged and skipped rather than thrown, same as the live effect — one bad file must not
// drop the rest of the batch. But if *every* copy in the batch fails, this must throw rather than
// return normally: checkAndConsumeRecovery treats a clean return as "imported, delete the
// marker" — silently succeeding on a total failure would drop the result for good.
const importImage: RecoveryImporter = async (ctx, result, { addAsset }) => {
const paths = Array.isArray(result) ? result : [result]
const s = ctx.settings
const genMode = ctx.inputImageUrl ? 'image-edit' : 'text-to-image'
let importedAny = false
for (const imgPath of paths) {
const copied = await addVisualAssetToProject(imgPath, ctx.projectId, 'image')
if (!copied) {
logger.error(`Could not persist generated image to project storage: ${imgPath}`)
continue
}
importedAny = true
addAsset(ctx.projectId, {
type: 'image',
path: copied.path,
bigThumbnailPath: copied.bigThumbnailPath,
smallThumbnailPath: copied.smallThumbnailPath,
width: copied.width,
height: copied.height,
prompt: ctx.prompt,
resolution: s?.imageResolution ?? '',
generationParams: {
mode: genMode,
prompt: ctx.prompt,
model: 'fast',
duration: 5,
resolution: s?.imageResolution ?? '',
fps: 24,
audio: false,
cameraMotion: 'none',
imageAspectRatio: s?.aspectRatio,
imageSteps: s?.imageSteps ?? 4,
...(ctx.inputImageUrl ? { inputImageUrl: ctx.inputImageUrl, imageEditStrength: s?.imageEditStrength } : {}),
},
takes: [{
path: copied.path,
bigThumbnailPath: copied.bigThumbnailPath,
smallThumbnailPath: copied.smallThumbnailPath,
width: copied.width,
height: copied.height,
createdAt: Date.now(),
}],
activeTakeIndex: 0,
})
}
if (!importedAny && paths.length > 0) {
throw new Error('Could not persist any generated image to project storage')
}
}
export const builtinRecoveryImporters: Partial<Record<RecoveryGenType, RecoveryImporter>> = {
video: importVideo,
image: importImage,
}