Spaces:
Runtime error
Runtime error
Julian Bilcke
commited on
Commit
·
e0c4dc2
1
Parent(s):
9f14b6c
ok, I think it works now
Browse files- src/app/engine/render.ts +22 -25
- src/app/games/vernian.ts +2 -2
- src/app/interface/top-menu/index.tsx +15 -3
- src/app/main.tsx +41 -9
- src/lib/fonts.ts +16 -1
- src/types.ts +4 -0
src/app/engine/render.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
-
import { RenderedScene } from "@/types"
|
| 6 |
import { Engine, EngineType } from "./engines"
|
| 7 |
|
| 8 |
// note: there is no / at the end in the variable
|
|
@@ -15,10 +15,12 @@ export async function newRender({
|
|
| 15 |
prompt,
|
| 16 |
actionnables = [],
|
| 17 |
engine,
|
|
|
|
| 18 |
}: {
|
| 19 |
prompt: string
|
| 20 |
actionnables: string[]
|
| 21 |
engine: Engine
|
|
|
|
| 22 |
}) {
|
| 23 |
if (!prompt) {
|
| 24 |
console.error(`cannot call the rendering API without a prompt, aborting..`)
|
|
@@ -31,8 +33,6 @@ export async function newRender({
|
|
| 31 |
|
| 32 |
const nbFrames = engine.type.includes("video") ? 8 : 1
|
| 33 |
|
| 34 |
-
const cacheKey = `render/${JSON.stringify({ prompt, actionnables, nbFrames, type: engine.type })}`
|
| 35 |
-
|
| 36 |
// return await Gorgon.get(cacheKey, async () => {
|
| 37 |
|
| 38 |
let defaulResult: RenderedScene = {
|
|
@@ -49,18 +49,24 @@ export async function newRender({
|
|
| 49 |
|
| 50 |
const isForVideo = nbFrames > 1
|
| 51 |
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
const res = await fetch(`${apiUrl}/render`, {
|
| 65 |
method: "POST",
|
| 66 |
headers: {
|
|
@@ -68,16 +74,7 @@ export async function newRender({
|
|
| 68 |
"Content-Type": "application/json",
|
| 69 |
// Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
|
| 70 |
},
|
| 71 |
-
body: JSON.stringify(
|
| 72 |
-
prompt,
|
| 73 |
-
// nbFrames: 8 and nbSteps: 15 --> ~10 sec generation
|
| 74 |
-
nbFrames, // when nbFrames is 1, we will only generate static images
|
| 75 |
-
nbSteps: isForVideo ? 30 : 35, // 20 = fast, 30 = better, 50 = best
|
| 76 |
-
actionnables,
|
| 77 |
-
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
| 78 |
-
width: isForVideo ? 576 : 1024,
|
| 79 |
-
height: isForVideo ? 320 : 768,
|
| 80 |
-
}),
|
| 81 |
cache: 'no-store',
|
| 82 |
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
| 83 |
// next: { revalidate: 1 }
|
|
|
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
+
import { RenderRequest, RenderedScene } from "@/types"
|
| 6 |
import { Engine, EngineType } from "./engines"
|
| 7 |
|
| 8 |
// note: there is no / at the end in the variable
|
|
|
|
| 15 |
prompt,
|
| 16 |
actionnables = [],
|
| 17 |
engine,
|
| 18 |
+
clearCache,
|
| 19 |
}: {
|
| 20 |
prompt: string
|
| 21 |
actionnables: string[]
|
| 22 |
engine: Engine
|
| 23 |
+
clearCache: boolean
|
| 24 |
}) {
|
| 25 |
if (!prompt) {
|
| 26 |
console.error(`cannot call the rendering API without a prompt, aborting..`)
|
|
|
|
| 33 |
|
| 34 |
const nbFrames = engine.type.includes("video") ? 8 : 1
|
| 35 |
|
|
|
|
|
|
|
| 36 |
// return await Gorgon.get(cacheKey, async () => {
|
| 37 |
|
| 38 |
let defaulResult: RenderedScene = {
|
|
|
|
| 49 |
|
| 50 |
const isForVideo = nbFrames > 1
|
| 51 |
|
| 52 |
+
const request = {
|
| 53 |
+
prompt,
|
| 54 |
+
// nbFrames: 8 and nbSteps: 15 --> ~10 sec generation
|
| 55 |
+
nbFrames, // when nbFrames is 1, we will only generate static images
|
| 56 |
+
nbSteps: isForVideo ? 30 : 35, // 20 = fast, 30 = better, 50 = best
|
| 57 |
+
actionnables,
|
| 58 |
+
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
| 59 |
+
width: isForVideo ? 576 : 1024,
|
| 60 |
+
height: isForVideo ? 320 : 768,
|
| 61 |
+
|
| 62 |
+
// note that we never disable the cache completely for VideoQuest
|
| 63 |
+
// that's because in the feedbacks people prefer speed to avoid frustration
|
| 64 |
+
cache: clearCache ? "renew" : "use",
|
| 65 |
+
|
| 66 |
+
} as Partial<RenderRequest>
|
| 67 |
+
|
| 68 |
+
console.table(request)
|
| 69 |
+
|
| 70 |
const res = await fetch(`${apiUrl}/render`, {
|
| 71 |
method: "POST",
|
| 72 |
headers: {
|
|
|
|
| 74 |
"Content-Type": "application/json",
|
| 75 |
// Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
|
| 76 |
},
|
| 77 |
+
body: JSON.stringify(request),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
cache: 'no-store',
|
| 79 |
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
| 80 |
// next: { revalidate: 1 }
|
src/app/games/vernian.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import {
|
| 2 |
import { Game } from "./types"
|
| 3 |
import { InventoryItem } from "../../types"
|
| 4 |
|
|
@@ -79,7 +79,7 @@ export const game: Game = {
|
|
| 79 |
"cartesian_video",
|
| 80 |
"spherical_image",
|
| 81 |
],
|
| 82 |
-
className:
|
| 83 |
initialSituation,
|
| 84 |
initialActionnables,
|
| 85 |
inventory,
|
|
|
|
| 1 |
+
import { moondance } from "@/lib/fonts"
|
| 2 |
import { Game } from "./types"
|
| 3 |
import { InventoryItem } from "../../types"
|
| 4 |
|
|
|
|
| 79 |
"cartesian_video",
|
| 80 |
"spherical_image",
|
| 81 |
],
|
| 82 |
+
className: moondance.className,
|
| 83 |
initialSituation,
|
| 84 |
initialActionnables,
|
| 85 |
inventory,
|
src/app/interface/top-menu/index.tsx
CHANGED
|
@@ -19,18 +19,23 @@ export function TopMenu({
|
|
| 19 |
engine,
|
| 20 |
defaultGame,
|
| 21 |
game,
|
|
|
|
|
|
|
| 22 |
onChangeEngine,
|
| 23 |
onChangeGame,
|
| 24 |
onToggleDebug,
|
| 25 |
-
|
| 26 |
}: {
|
| 27 |
engine: Engine,
|
| 28 |
defaultGame: string
|
| 29 |
game: Game
|
|
|
|
|
|
|
| 30 |
onChangeEngine: (newEngine: EngineType) => void
|
| 31 |
onChangeGame: (newGameType: GameType) => void
|
| 32 |
onToggleDebug: (isToggledOn: boolean) => void
|
| 33 |
-
|
|
|
|
| 34 |
}) {
|
| 35 |
return (
|
| 36 |
<div className={cn(
|
|
@@ -65,7 +70,14 @@ export function TopMenu({
|
|
| 65 |
<Label>Debug</Label>
|
| 66 |
</div>
|
| 67 |
<div className="flex flex-row items-center space-x-3 font-mono">
|
| 68 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
<Select
|
| 70 |
defaultValue={game.engines.includes(engine.type) ? engine.type : game.engines[0]}
|
| 71 |
onValueChange={(value) => { onChangeEngine(value as EngineType) }}>
|
|
|
|
| 19 |
engine,
|
| 20 |
defaultGame,
|
| 21 |
game,
|
| 22 |
+
debug,
|
| 23 |
+
clearCache,
|
| 24 |
onChangeEngine,
|
| 25 |
onChangeGame,
|
| 26 |
onToggleDebug,
|
| 27 |
+
onToggleClearCache,
|
| 28 |
}: {
|
| 29 |
engine: Engine,
|
| 30 |
defaultGame: string
|
| 31 |
game: Game
|
| 32 |
+
debug: boolean
|
| 33 |
+
clearCache: boolean
|
| 34 |
onChangeEngine: (newEngine: EngineType) => void
|
| 35 |
onChangeGame: (newGameType: GameType) => void
|
| 36 |
onToggleDebug: (isToggledOn: boolean) => void
|
| 37 |
+
onToggleClearCache: (shouldClearCache: boolean) => void
|
| 38 |
+
|
| 39 |
}) {
|
| 40 |
return (
|
| 41 |
<div className={cn(
|
|
|
|
| 70 |
<Label>Debug</Label>
|
| 71 |
</div>
|
| 72 |
<div className="flex flex-row items-center space-x-3 font-mono">
|
| 73 |
+
<Switch
|
| 74 |
+
checked={clearCache}
|
| 75 |
+
onCheckedChange={onToggleClearCache}
|
| 76 |
+
/>
|
| 77 |
+
<Label>No cache</Label>
|
| 78 |
+
</div>
|
| 79 |
+
<div className="flex flex-row items-center space-x-3 font-mono">
|
| 80 |
+
<Label className="flex text-sm">Engine:</Label>
|
| 81 |
<Select
|
| 82 |
defaultValue={game.engines.includes(engine.type) ? engine.type : game.engines[0]}
|
| 83 |
onValueChange={(value) => { onChangeEngine(value as EngineType) }}>
|
src/app/main.tsx
CHANGED
|
@@ -55,6 +55,9 @@ export default function Main() {
|
|
| 55 |
const requestedDebug = (searchParams.get('debug') === "true")
|
| 56 |
const [debug, setDebug] = useState<boolean>(requestedDebug)
|
| 57 |
|
|
|
|
|
|
|
|
|
|
| 58 |
const [situation, setSituation] = useState("")
|
| 59 |
|
| 60 |
const [dialogue, setDialogue] = useState("")
|
|
@@ -91,10 +94,10 @@ export default function Main() {
|
|
| 91 |
Array.isArray(nextActionnables) && nextActionnables.length
|
| 92 |
? nextActionnables
|
| 93 |
: game.initialActionnables
|
| 94 |
-
)
|
| 95 |
-
})
|
| 96 |
|
| 97 |
-
|
|
|
|
| 98 |
|
| 99 |
// detect if type game type changed while we were busy
|
| 100 |
// note that currently we reload the whol page when tha happens,
|
|
@@ -102,15 +105,25 @@ export default function Main() {
|
|
| 102 |
if (game?.type !== gameRef?.current) {
|
| 103 |
console.log("game type changed! aborting..")
|
| 104 |
return
|
|
|
|
|
|
|
|
|
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
}
|
| 107 |
-
// we cheat a bit by displaying the previous image as a placeholder
|
| 108 |
-
// this is better than displaying a blank image!
|
| 109 |
-
newRendered.assetUrl = rendered.assetUrl
|
| 110 |
-
newRendered.maskUrl = rendered.maskUrl
|
| 111 |
|
| 112 |
historyRef.current.unshift(newRendered)
|
| 113 |
setRendered(newRendered)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
})
|
| 115 |
}
|
| 116 |
|
|
@@ -121,7 +134,7 @@ export default function Main() {
|
|
| 121 |
if (!historyRef.current[0]?.renderId || historyRef.current[0]?.status !== "pending") {
|
| 122 |
|
| 123 |
// console.log("let's try again in a moments")
|
| 124 |
-
loopRef.current = setTimeout(() => checkRenderedLoop(),
|
| 125 |
return
|
| 126 |
}
|
| 127 |
|
|
@@ -255,6 +268,23 @@ export default function Main() {
|
|
| 255 |
setDebug(isToggledOn)
|
| 256 |
}
|
| 257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
const handleSelectGame = (newGameType: GameType) => {
|
| 259 |
gameRef.current = newGameType
|
| 260 |
setGame(getGame(newGameType))
|
|
@@ -412,10 +442,12 @@ export default function Main() {
|
|
| 412 |
engine={engine}
|
| 413 |
game={game}
|
| 414 |
defaultGame={gameRef.current}
|
|
|
|
|
|
|
| 415 |
onToggleDebug={handleToggleDebug}
|
| 416 |
onChangeGame={handleSelectGame}
|
| 417 |
onChangeEngine={handleSelectEngine}
|
| 418 |
-
|
| 419 |
/>
|
| 420 |
|
| 421 |
<DndProvider backend={HTML5Backend}>
|
|
|
|
| 55 |
const requestedDebug = (searchParams.get('debug') === "true")
|
| 56 |
const [debug, setDebug] = useState<boolean>(requestedDebug)
|
| 57 |
|
| 58 |
+
const requestedClearCache = (searchParams.get('clearCache') ? (searchParams.get('clearCache') === "true") : false)
|
| 59 |
+
const [clearCache, setClearCache] = useState<boolean>(requestedClearCache)
|
| 60 |
+
|
| 61 |
const [situation, setSituation] = useState("")
|
| 62 |
|
| 63 |
const [dialogue, setDialogue] = useState("")
|
|
|
|
| 94 |
Array.isArray(nextActionnables) && nextActionnables.length
|
| 95 |
? nextActionnables
|
| 96 |
: game.initialActionnables
|
| 97 |
+
),
|
|
|
|
| 98 |
|
| 99 |
+
clearCache
|
| 100 |
+
})
|
| 101 |
|
| 102 |
// detect if type game type changed while we were busy
|
| 103 |
// note that currently we reload the whol page when tha happens,
|
|
|
|
| 105 |
if (game?.type !== gameRef?.current) {
|
| 106 |
console.log("game type changed! aborting..")
|
| 107 |
return
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
console.log("got the first version of our scene!", newRendered)
|
| 111 |
|
| 112 |
+
// in cache we didn't hit the cache and the request is still pending
|
| 113 |
+
// we cheat a bit by displaying the previous image as a placeholder
|
| 114 |
+
if (!newRendered.assetUrl && rendered.assetUrl && rendered.maskUrl) {
|
| 115 |
+
console.log("image is not in cache, using previous image as a placeholder..")
|
| 116 |
+
// this is better than displaying a blank image, no?
|
| 117 |
+
newRendered.assetUrl = rendered.assetUrl
|
| 118 |
+
newRendered.maskUrl = rendered.maskUrl
|
| 119 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
historyRef.current.unshift(newRendered)
|
| 122 |
setRendered(newRendered)
|
| 123 |
+
|
| 124 |
+
if (newRendered.status === "completed") {
|
| 125 |
+
setBusy(busyRef.current = false)
|
| 126 |
+
}
|
| 127 |
})
|
| 128 |
}
|
| 129 |
|
|
|
|
| 134 |
if (!historyRef.current[0]?.renderId || historyRef.current[0]?.status !== "pending") {
|
| 135 |
|
| 136 |
// console.log("let's try again in a moments")
|
| 137 |
+
loopRef.current = setTimeout(() => checkRenderedLoop(), 1000)
|
| 138 |
return
|
| 139 |
}
|
| 140 |
|
|
|
|
| 268 |
setDebug(isToggledOn)
|
| 269 |
}
|
| 270 |
|
| 271 |
+
const handleToggleClearCache = (shouldClearCache: boolean) => {
|
| 272 |
+
const current = new URLSearchParams(Array.from(searchParams.entries()))
|
| 273 |
+
current.set("clearCache", `${shouldClearCache}`)
|
| 274 |
+
const search = current.toString()
|
| 275 |
+
const query = search ? `?${search}` : ""
|
| 276 |
+
|
| 277 |
+
// for some reason, this doesn't work?!
|
| 278 |
+
router.replace(`${pathname}${query}`, { })
|
| 279 |
+
|
| 280 |
+
// workaround.. but it is strange that router.replace doesn't work..
|
| 281 |
+
let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()
|
| 282 |
+
window.history.pushState({path: newurl}, '', newurl)
|
| 283 |
+
|
| 284 |
+
setClearCache(shouldClearCache)
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
const handleSelectGame = (newGameType: GameType) => {
|
| 289 |
gameRef.current = newGameType
|
| 290 |
setGame(getGame(newGameType))
|
|
|
|
| 442 |
engine={engine}
|
| 443 |
game={game}
|
| 444 |
defaultGame={gameRef.current}
|
| 445 |
+
debug={debug}
|
| 446 |
+
clearCache={clearCache}
|
| 447 |
onToggleDebug={handleToggleDebug}
|
| 448 |
onChangeGame={handleSelectGame}
|
| 449 |
onChangeEngine={handleSelectEngine}
|
| 450 |
+
onToggleClearCache={handleToggleClearCache}
|
| 451 |
/>
|
| 452 |
|
| 453 |
<DndProvider backend={HTML5Backend}>
|
src/lib/fonts.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
| 1 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import localFont from "next/font/local"
|
| 3 |
|
| 4 |
export const inter = Inter({
|
|
@@ -11,6 +19,13 @@ export const edu = Edu_SA_Beginner({
|
|
| 11 |
variable: "--font-edu",
|
| 12 |
})
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
export const orbitron = Orbitron({
|
| 15 |
subsets: ["latin"],
|
| 16 |
variable: "--font-orbitron",
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Inter,
|
| 3 |
+
Edu_SA_Beginner,
|
| 4 |
+
Orbitron,
|
| 5 |
+
Amatic_SC,
|
| 6 |
+
Macondo_Swash_Caps,
|
| 7 |
+
IM_Fell_English_SC,
|
| 8 |
+
Moon_Dance
|
| 9 |
+
} from "next/font/google"
|
| 10 |
import localFont from "next/font/local"
|
| 11 |
|
| 12 |
export const inter = Inter({
|
|
|
|
| 19 |
variable: "--font-edu",
|
| 20 |
})
|
| 21 |
|
| 22 |
+
export const moondance = Moon_Dance({
|
| 23 |
+
subsets: ["latin"],
|
| 24 |
+
weight: "400",
|
| 25 |
+
variable: "--font-moondance",
|
| 26 |
+
})
|
| 27 |
+
|
| 28 |
+
|
| 29 |
export const orbitron = Orbitron({
|
| 30 |
subsets: ["latin"],
|
| 31 |
variable: "--font-orbitron",
|
src/types.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
export type ProjectionMode = 'cartesian' | 'spherical'
|
| 2 |
|
|
|
|
|
|
|
| 3 |
export interface RenderRequest {
|
| 4 |
prompt: string
|
| 5 |
|
|
@@ -28,6 +30,8 @@ export interface RenderRequest {
|
|
| 28 |
height: number // fixed at 512 for now
|
| 29 |
|
| 30 |
projection: ProjectionMode
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
|
| 33 |
export interface ImageSegment {
|
|
|
|
| 1 |
export type ProjectionMode = 'cartesian' | 'spherical'
|
| 2 |
|
| 3 |
+
export type CacheMode = "use" | "renew" | "ignore"
|
| 4 |
+
|
| 5 |
export interface RenderRequest {
|
| 6 |
prompt: string
|
| 7 |
|
|
|
|
| 30 |
height: number // fixed at 512 for now
|
| 31 |
|
| 32 |
projection: ProjectionMode
|
| 33 |
+
|
| 34 |
+
cache: CacheMode
|
| 35 |
}
|
| 36 |
|
| 37 |
export interface ImageSegment {
|