Add panorama previews and repair beta export rendering
This commit is contained in:
@@ -18,7 +18,7 @@ async function assetsReady() {
|
||||
try {
|
||||
const marker = JSON.parse(await readFile(completionMarker, "utf8"));
|
||||
if (
|
||||
marker.format !== "minegeo-assets/v1" ||
|
||||
marker.format !== "minegeo-assets/v2" ||
|
||||
marker.minecraftVersion !== "1.21.11" ||
|
||||
!marker.files
|
||||
)
|
||||
@@ -89,7 +89,7 @@ export async function prepareAssets() {
|
||||
filter: (f) =>
|
||||
/^assets\/minecraft\/(models|blockstates|textures\/block)\//.test(
|
||||
f.name,
|
||||
) && /\.(json|png)$/.test(f.name),
|
||||
) && /\.(json|png|mcmeta)$/.test(f.name),
|
||||
});
|
||||
for (const [name, bytes] of Object.entries(entries)) {
|
||||
const path = `${assetsRoot}/${name}`;
|
||||
@@ -100,7 +100,7 @@ export async function prepareAssets() {
|
||||
await writeFile(
|
||||
completionMarker,
|
||||
JSON.stringify({
|
||||
format: "minegeo-assets/v1",
|
||||
format: "minegeo-assets/v2",
|
||||
minecraftVersion: "1.21.11",
|
||||
clientSha1: version.downloads.client.sha1,
|
||||
files: Object.keys(entries).length,
|
||||
|
||||
147
scripts/repair-panoramas.mjs
Normal file
147
scripts/repair-panoramas.mjs
Normal file
@@ -0,0 +1,147 @@
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync } from "node:fs";
|
||||
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { gunzipSync } from "node:zlib";
|
||||
import { renderSnapshot } from "../renderer/render.mjs";
|
||||
|
||||
if (existsSync(".env.local")) process.loadEnvFile(".env.local");
|
||||
const id = process.argv[2];
|
||||
if (
|
||||
!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(
|
||||
id || "",
|
||||
)
|
||||
)
|
||||
throw new Error(
|
||||
"Usage: npm run repair:panoramas -- <private plugin draft UUID>",
|
||||
);
|
||||
const client = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.SUPABASE_SECRET_KEY,
|
||||
{ auth: { persistSession: false, autoRefreshToken: false } },
|
||||
);
|
||||
function check(r) {
|
||||
if (r.error) throw new Error(r.error.message);
|
||||
return r.data;
|
||||
}
|
||||
const dataset = check(
|
||||
await client.from("datasets").select("*").eq("id", id).single(),
|
||||
);
|
||||
if (
|
||||
dataset.source !== "plugin" ||
|
||||
dataset.revision_id ||
|
||||
!["draft", "rejected"].includes(dataset.state)
|
||||
)
|
||||
throw new Error(
|
||||
"Only a private, unpublished plugin draft can be repaired. Withdraw review or recover failed processing first.",
|
||||
);
|
||||
const job = check(
|
||||
await client
|
||||
.from("upload_jobs")
|
||||
.select("state")
|
||||
.eq("dataset_id", id)
|
||||
.single(),
|
||||
);
|
||||
if (job.state !== "complete")
|
||||
throw new Error("Wait for processing to finish first.");
|
||||
const input = check(
|
||||
await client
|
||||
.from("dataset_inputs")
|
||||
.select("manifest,validated")
|
||||
.eq("dataset_id", id)
|
||||
.single(),
|
||||
);
|
||||
if (!input.validated) throw new Error("The export must be validated first.");
|
||||
const asset = input.manifest.assets.find((a) => a.path === "terrain.json.gz");
|
||||
if (!asset) throw new Error("Missing original terrain export");
|
||||
const raw = check(
|
||||
await client.storage
|
||||
.from("minegeo-raw")
|
||||
.download(`${dataset.owner_id}/${id}/${asset.path}`),
|
||||
);
|
||||
const bytes = Buffer.from(await raw.arrayBuffer());
|
||||
if (
|
||||
bytes.length !== asset.bytes ||
|
||||
createHash("sha256").update(bytes).digest("hex") !== asset.sha256
|
||||
)
|
||||
throw new Error("Original terrain checksum mismatch");
|
||||
const output = `.cache/panorama-repair/${id}`;
|
||||
await mkdir(output, { recursive: true });
|
||||
const terrain = JSON.parse(
|
||||
gunzipSync(bytes, { maxOutputLength: 256 * 1024 * 1024 }).toString(),
|
||||
);
|
||||
await renderSnapshot(
|
||||
terrain,
|
||||
input.manifest.availableViewpoints ?? input.manifest.viewpoints,
|
||||
output,
|
||||
{
|
||||
size: Number(process.env.MINEGEO_FACE_SIZE || 1024),
|
||||
onProgress: async ({ percent, message }) =>
|
||||
console.log(`${Math.floor(percent)}% ${message}`),
|
||||
},
|
||||
);
|
||||
// Render first, then briefly lock the draft while replacing its private images.
|
||||
// Its complete job stays complete, so another worker cannot pick it up.
|
||||
const locked = check(
|
||||
await client
|
||||
.from("datasets")
|
||||
.update({ state: "processing", updated_at: new Date().toISOString() })
|
||||
.eq("id", id)
|
||||
.eq("state", dataset.state)
|
||||
.eq("updated_at", dataset.updated_at)
|
||||
.is("revision_id", null)
|
||||
.select("id"),
|
||||
);
|
||||
if (locked.length !== 1)
|
||||
throw new Error(
|
||||
"Draft changed during rendering; images were retained locally but nothing was replaced.",
|
||||
);
|
||||
try {
|
||||
for (const entry of (await readdir(output, { recursive: true })).filter((p) =>
|
||||
p.endsWith(".webp"),
|
||||
)) {
|
||||
check(
|
||||
await client.storage
|
||||
.from("minegeo-media")
|
||||
.upload(
|
||||
`${id}/${entry.replaceAll("\\", "/")}`,
|
||||
await readFile(`${output}/${entry}`),
|
||||
{ upsert: true, contentType: "image/webp", cacheControl: "60" },
|
||||
),
|
||||
);
|
||||
}
|
||||
check(
|
||||
await client
|
||||
.from("datasets")
|
||||
.update({ state: dataset.state, updated_at: new Date().toISOString() })
|
||||
.eq("id", id)
|
||||
.eq("state", "processing")
|
||||
.is("revision_id", null)
|
||||
.select("id")
|
||||
.single(),
|
||||
);
|
||||
await writeFile(
|
||||
`${output}/repair.json`,
|
||||
JSON.stringify({
|
||||
datasetId: id,
|
||||
repaired: true,
|
||||
at: new Date().toISOString(),
|
||||
}),
|
||||
);
|
||||
console.log(
|
||||
`Repaired draft ${id}. Reload its dashboard preview; no Minecraft export or new upload is needed.`,
|
||||
);
|
||||
} catch (error) {
|
||||
await client
|
||||
.from("datasets")
|
||||
.update({
|
||||
state: "failed",
|
||||
rejection_reason:
|
||||
"Image repair interrupted. Reprocess the retained raw upload.",
|
||||
updated_at: new Date().toISOString(),
|
||||
})
|
||||
.eq("id", id)
|
||||
.eq("state", "processing")
|
||||
.is("revision_id", null);
|
||||
throw error;
|
||||
}
|
||||
Reference in New Issue
Block a user