Kay
Kay9mo ago

Get image from automated download

I have this page which displays a render using threejs and renders it into a png and downloads it. i need that image to use it in a canvas from deno-canvas but when i download it using this package it returns this:
error: Uncaught (in promise) Error: The filename, directory name, or volume label syntax is incorrect. (os error 123): writefile 'C:\Users\titul\AppData\Local\Temp\deno_dwld2b5d29e3bc7dd136/E:\Arcunis\recipeImageMaker\stone.png'
error: Uncaught (in promise) Error: The filename, directory name, or volume label syntax is incorrect. (os error 123): writefile 'C:\Users\titul\AppData\Local\Temp\deno_dwld2b5d29e3bc7dd136/E:\Arcunis\recipeImageMaker\stone.png'
code where i download it:|
import { download } from "https://deno.land/x/download@v2.0.2/mod.ts";
import { join } from "https://deno.land/std/path/mod.ts";
export default async function generateBlock(block: String) {
await download("http://localhost:8000/renderer/index.html?material=" + block, {file: join(Deno.cwd(), block + ".png")});
}
import { download } from "https://deno.land/x/download@v2.0.2/mod.ts";
import { join } from "https://deno.land/std/path/mod.ts";
export default async function generateBlock(block: String) {
await download("http://localhost:8000/renderer/index.html?material=" + block, {file: join(Deno.cwd(), block + ".png")});
}
21 Replies
marvinh.
marvinh.9mo ago
Looking at the docs of the download library the file parameter is there to set the filename only. You're passing a full absolute path into that. Under the hood the library seem to only append the file parameter to their own internal path leading to the broken file path you're seeing in the error message. You might have better luck just using builtin APIs to download a file:
async function download(url: string, filePath: string) {
const res = await fetch(url);
const file = await Deno.open(filePath, {
create: true,
write: true
})

await res.body?.pipeTo(file.writable);
}

// Usage
await download("https://example.com/my-file.txt", path.join(Deno.cwd(), "save-to-disk.txt"))
async function download(url: string, filePath: string) {
const res = await fetch(url);
const file = await Deno.open(filePath, {
create: true,
write: true
})

await res.body?.pipeTo(file.writable);
}

// Usage
await download("https://example.com/my-file.txt", path.join(Deno.cwd(), "save-to-disk.txt"))
Kay
KayOP9mo ago
this still gives an error
error: Uncaught (in promise) BadResource: Bad resource ID
file.close();
^
at FsFile.close (ext:deno_fs/30_fs.js:738:10)
at download (file:///E:/Arcunis/recipeImageMaker/src/generateBlock.ts:11:10)
at eventLoopTick (ext:core/01_core.js:153:7)
at async generateBlock (file:///E:/Arcunis/recipeImageMaker/src/generateBlock.ts:16:5)
error: Uncaught (in promise) BadResource: Bad resource ID
file.close();
^
at FsFile.close (ext:deno_fs/30_fs.js:738:10)
at download (file:///E:/Arcunis/recipeImageMaker/src/generateBlock.ts:11:10)
at eventLoopTick (ext:core/01_core.js:153:7)
at async generateBlock (file:///E:/Arcunis/recipeImageMaker/src/generateBlock.ts:16:5)
new file:
import { join } from "https://deno.land/std@0.93.0/path/mod.ts";

async function download(url: string, filePath: string) {
const res = await fetch(url);
const file = await Deno.open(filePath, {
create: true,
write: true
})

await res.body?.pipeTo(file.writable);
file.close();
}

export default async function generateBlock(block: String) {

await download("http://localhost:8000/renderer/index.html?material=" + block, join(Deno.cwd(), block + ".png"));

}

generateBlock("stone"); // Temporary function call for testing
import { join } from "https://deno.land/std@0.93.0/path/mod.ts";

async function download(url: string, filePath: string) {
const res = await fetch(url);
const file = await Deno.open(filePath, {
create: true,
write: true
})

await res.body?.pipeTo(file.writable);
file.close();
}

export default async function generateBlock(block: String) {

await download("http://localhost:8000/renderer/index.html?material=" + block, join(Deno.cwd(), block + ".png"));

}

generateBlock("stone"); // Temporary function call for testing
it does look like progress tho
marvinh.
marvinh.9mo ago
@Kay ah apologies, the file.close() should be removed Deleted it from my code snippet. Not sure why I added that, my brain blanked probably
Kay
KayOP9mo ago
that worked. only the image is not valid. the stone-valid.png is downloaded by me just going to the url and the stone-invalid.png is downloaded by the code. the file sizes do not match and the invalid one does not load in any image editor/viewer
No description
zamfofex
zamfofex9mo ago
marvinh’s code works for me. Are you sure the file being served is the same?
Kay
KayOP9mo ago
its not exactly being served. its generated. first threejs generates a cube and loads a texture from a material from the url parameters. then it generates an image and downloads it
marvinh.
marvinh.9mo ago
sounds like something is wrong with how the image is generated
Kay
KayOP9mo ago
could be but if i just go to the page myself it works fine
Kay
KayOP9mo ago
Gist
index.html
GitHub Gist: instantly share code, notes, and snippets.
zamfofex
zamfofex9mo ago
It seems like you’re actually downloading the page’s source instead of the image you want. I’m not sure you can easily/seamlessly just have Deno save the file from the browser JavaScript code.
Kay
KayOP9mo ago
ahh ye when i opened the file i do indeed see the html
zamfofex
zamfofex9mo ago
But note that you can just run that code on Deno itself if you use the right tools, instead of having it run on a browser. I guess Three.js might not be possible, actually. 🤔
Kay
KayOP9mo ago
yhea only i coudnt find a local kind of version of threejs or something like it
zamfofex
zamfofex9mo ago
If you just want an isometric image, you could create it without Three.js.
Kay
KayOP9mo ago
how do you mean?
Kay
KayOP9mo ago
also this is an exaple output of the renderer just a 3D cube with a texture captured using an orthographic camera
No description
Kay
KayOP9mo ago
the problem is that its still 3D and it has directional light @zamfofex any idea if there is a package that can let me render things in the deno environment instead of using html? or something that can create images with the possibility of loading textures with corner positions (idk what its officialy called) to be able to make it look 3D or mabye be able to export the threejs render in a different way so i can access it
zamfofex
zamfofex9mo ago
See if this is satisfying enough! deno run -A cube.js
Kay
KayOP9mo ago
ohh thats cool. already figured out another way tho. mabye a bit slower but idc abb speed. i made the renderer set the textContent to the dataUrl and use puppeteer to load the page, wait for the data to be put in the body and turn the data into an image. got another problem now is that the image wont show. this is the code where it puts the items on the crafting grid:
let i = 0;
for (const item of recipe.items) {
if (item == null) {
i ++;
continue;
}
const itemTexture: Image = await getTexture(item);
console.log(itemTexture.width(), itemTexture.height());
ctx.drawImage(itemTexture, slots[i][0] * recipe.size_multiplier, slots[i][1] * recipe.size_multiplier, itemTexture.width() * recipe.size_multiplier, itemTexture.height() * recipe.size_multiplier);
i ++;
}
let i = 0;
for (const item of recipe.items) {
if (item == null) {
i ++;
continue;
}
const itemTexture: Image = await getTexture(item);
console.log(itemTexture.width(), itemTexture.height());
ctx.drawImage(itemTexture, slots[i][0] * recipe.size_multiplier, slots[i][1] * recipe.size_multiplier, itemTexture.width() * recipe.size_multiplier, itemTexture.height() * recipe.size_multiplier);
i ++;
}
getTexture function:
async function getTexture(item: string): Promise<Image> {
const itemModel: ItemModel = await import("./assets/minecraft/models/item/" + item + ".json", {with: {type: "json"}}).then(value => value.default);
if (itemModel.parent == "minecraft:item/generated") {
return await loadImage("./assets/minecraft/textures/" + itemModel.textures.layer0.substring(10) + ".png");
}
const parentModel: ItemModel = await import("./assets/minecraft/models/" + itemModel.parent.substring(10) + ".json", {with: {type: "json"}}).then(value => value.default);
if (parentModel.parent == "minecraft:block/cube_all") {
return await generateBlock(item);
}
throw `unsupported model format '${itemModel.parent}'`
}
async function getTexture(item: string): Promise<Image> {
const itemModel: ItemModel = await import("./assets/minecraft/models/item/" + item + ".json", {with: {type: "json"}}).then(value => value.default);
if (itemModel.parent == "minecraft:item/generated") {
return await loadImage("./assets/minecraft/textures/" + itemModel.textures.layer0.substring(10) + ".png");
}
const parentModel: ItemModel = await import("./assets/minecraft/models/" + itemModel.parent.substring(10) + ".json", {with: {type: "json"}}).then(value => value.default);
if (parentModel.parent == "minecraft:block/cube_all") {
return await generateBlock(item);
}
throw `unsupported model format '${itemModel.parent}'`
}
and here it loads the image:
export default async function generateBlock(block: string): Promise<Image> {

if (!existsSync("./tmp/" + block + ".png")) {
if (!existsSync("./tmp")) Deno.mkdirSync("./tmp");
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("http://localhost:8000/renderer/index.html?material=" + block);
await sleep(100);
const data = await page.evaluate(() => document.body.textContent);

const dataResponse = await fetch(data);
const file = await Deno.open("./tmp/" + block + ".png", {create: true, write: true});
await dataResponse.body?.pipeTo(file.writable);
}

return await loadImage("./tmp/" + block + ".png");
}
export default async function generateBlock(block: string): Promise<Image> {

if (!existsSync("./tmp/" + block + ".png")) {
if (!existsSync("./tmp")) Deno.mkdirSync("./tmp");
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("http://localhost:8000/renderer/index.html?material=" + block);
await sleep(100);
const data = await page.evaluate(() => document.body.textContent);

const dataResponse = await fetch(data);
const file = await Deno.open("./tmp/" + block + ".png", {create: true, write: true});
await dataResponse.body?.pipeTo(file.writable);
}

return await loadImage("./tmp/" + block + ".png");
}
Kay
KayOP9mo ago
the image is the result and the recipe.json file is the input
Kay
KayOP9mo ago
the width of the stone image is also logging just fine so it clearly has the image. its just not putting it in sorry nvm its working. had to change the image size to a standard of 16 (the width of the crafting grid) but thx for the help. still used part of your code to get the image from the data url