fdm/scripts/prepare-stream.js

55 lines
3.3 KiB
JavaScript

import { mkdir, writeFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import { gzipSync } from "node:zlib";
import { encodePath } from "../src/lib/full-codec.js";
export async function prepareStream(packed, source) {
const { metadata, buffer } = packed;
const version = createHash("sha256").update("fdm-stream-v2-shell24\n").update(source).digest("hex").slice(0, 16);
const base = new URL(`../public/data/stream/${version}/`, import.meta.url);
await mkdir(base, { recursive: true });
const lines = source.split(/\r?\n/);
const starts = [0, ...metadata.layers.map((layer) => layer.segment)].filter((v, i, a) => !i || v !== a[i - 1]);
const chunks = [];
const overview = [];
const allCommands = {};
for (let id = 0; id < starts.length; id++) {
const start = starts[id];
const end = starts[id + 1] ?? metadata.count;
const part = buffer.subarray(start * 12, end * 12);
const commands = {};
const shell = [];
for (let index = start; index < end; index++) {
const o = index * 12;
commands[buffer[o + 11]] = lines[buffer[o + 11] - 1].trim();
allCommands[buffer[o + 11]] = commands[buffer[o + 11]];
if (buffer[o + 8] > 0 && ["Outer wall", "Overhang wall", "Top surface", "Bottom surface"].includes(metadata.types[buffer[o + 10]])) shell.push(index);
}
const step = Math.max(1, Math.ceil(shell.length / 24));
for (let i = 0; i < shell.length; i += step) {
const index = shell[i];
overview.push(index, ...buffer.subarray(index * 12, index * 12 + 12));
}
chunks.push({ id, start, end, startTime: start ? buffer[(start - 1) * 12 + 6] : 0, endTime: buffer[(end - 1) * 12 + 6] });
await writeFile(new URL(`${id}.bin`, base), Buffer.from(part.buffer, part.byteOffset, part.byteLength));
await writeFile(new URL(`${id}.json`, base), JSON.stringify(commands));
}
await writeFile(new URL("overview.bin", base), Buffer.from(new Float32Array(overview).buffer));
await writeFile(new URL("full.bin", base), Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength));
await writeFile(new URL("commands.json", base), JSON.stringify(allCommands));
const fullVersion = createHash("sha256").update("fdm-full-v2-delta\n").update(source).digest("hex").slice(0, 16);
const fullBase = new URL(`../public/data/full/${fullVersion}/`, import.meta.url);
await mkdir(fullBase, { recursive: true });
const commandText = Array.from({ length: metadata.count }, (_, index) => allCommands[buffer[index * 12 + 11]]).join("\n") + "\n";
const full = { decodedSha256: createHash("sha256").update(Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)).digest("hex") };
for (const [key, content] of [["path", encodePath(buffer)], ["commands", Buffer.from(commandText)]]) {
const compressed = gzipSync(content, { level: 9 });
const file = `${key}.fdmz`;
full[key] = { file, bytes: compressed.length, sha256: createHash("sha256").update(compressed).digest("hex") };
await writeFile(new URL(file, fullBase), compressed);
}
await writeFile(new URL("../public/data/stream.json", import.meta.url), JSON.stringify({ ...metadata, version, chunks, fullVersion, full }));
console.log(`完整数据无损压缩:${((full.path.bytes + full.commands.bytes) / 1048576).toFixed(2)} MiB`);
console.log(`按层数据:${chunks.length} 块,概览 ${overview.length / 13} 段`);
}