jimp not sending saved image - node.js

I'm trying to get image manipulation setup for a discord bot of mine, using lib discord.js. I'm grabbing args[0] from the command, and adding that text onto the image, saving it, sending it, and then deleting it with fs. This is my code below:
const Jimp = require('jimp');
const fs = require('fs');
module.exports = {
// Information
name: 'test',
aliases: [],
description: "test af.",
usage: "<args>",
args: {
req: true,
min: 1,
},
// Function
run: async (client, command, msg, args) => {
Jimp.read("img/brain.jpg").then(function(image){
Jimp.loadFont(Jimp.FONT_SANS_32_BLACK).then(function (font) {
image.resize(542, 767);
image.print(font, 16, 22, args[0]);
image.write('brain.jpg');
});
});
await msg.channel.send({
files: [
"brain.jpg"
]
});
fs.unlinkSync('brain.jpg');
}
}
The problem we have is, when doing the command and adding the arguments, so for example: ".test argshere" it prints the text on the image and saves it, but when it tries to send the image it says it cannot find it in that directory, but if I go into the folder it is there. I believe it is trying to send before it has even saved yet. I've tried awaiting print, write and msg.channel.send but nothing seemed to work.
I'd also like to ask how I could add a sort of "text box" onto an image, so if there is too much letters in one line the text will go onto another line, as I don't want the text going over the memes. This is the image below:
brain image meme

You use two callbacks to write the file, and then immediately after put code that will run before the callbacks. This is an asynchronous problem. If you put the send code after your write function, that should fix your issue.
run: async (client, command, msg, args) => {
Jimp.read("img/brain.jpg").then(function(image) {
Jimp.loadFont(Jimp.FONT_SANS_32_BLACK).then(function(font) {
image.resize(542, 767);
image.print(font, 16, 22, args[0]);
image.write('brain.jpg');
});
});
// brain.jpg doesn't exist until the second callback from above is run.
await msg.channel.send({
files: [
"brain.jpg"
]
});
fs.unlinkSync('brain.jpg');
}

You should give where the image should be saved in which folder. You have to use the public folder inside which you should have the image folder where the image should be saved
So the code will be
image.write("public/image/brain.png");
and before it, if you are using express you should write
const express = require('express');
const app = express();
app.use(express.static("public"));
so here we are telling our server that we are using public folder

Related

Slash command registers command from wrong folder discord.js14

I'm tired of trying to solve this. First off, here is my deployment code
const { REST, Routes } = require('discord.js');
const fs = require('node:fs');
const { client_id } = require('./config.json')
const commands = [];
// Grab all the command files from the commands directory you created earlier
const commandFiles = fs.readdirSync('./slashCommands').filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const command = require(`./slashCommands/${file}`);
commands.push(command.data.toJSON());
}
// Construct and prepare an instance of the REST module
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);
// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationCommands(client_id),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
It is supposed to get the command from the "slashCommand" folder. So I run 'node deploy-commands.js' and it works.
The problem is when I do the slash command '/ping', I get this error:
/home/runner/Nocinel/commands/ping.js:8
message.reply('🏓 **Ball is going over the net...**').then(m => { m.edit(`**🏓 Pong!\n:stopwatch: Uptime: ${Math.round(message.client.uptime / 60000)} minutes\n:sparkling_heart: Websocket Heartbeat: ${message.client.ws.ping}ms\n:round_pushpin: Rountrip Latency: ${m.createdTimestamp - message.createdTimestamp}ms**`) });
^
TypeError: m.edit is not a function
at /home/runner/Nocinel/commands/ping.js:8:73
repl process died unexpectedly: exit status 1
Now this error indicates that I am running a command from my "command" folder rather than my "slashCommand" folder. Which doesnt make sense because I explicitly coded it to only get commands from the "slash command folder"
I have restarted, deleted, waited for an hour, and tested it multiple times, it always gives the same disappointing result. I see absolutely nothing wrong with my code.
There is no problem with registring comannd (deploy-comannds.js is only registring comannds not using making them work). Problem have to be in your index.js you have to handle interaction comannds to your folder slashComannds. Registring comannds was sucessfull.
Documentation:
https://discordjs.guide/creating-your-bot/command-handling.html#loading-command-files

use .sfz soundfonts to render audio with WebMScore

I'm using WebMScore to render audio of music scores (it's a fork of MuseScore that runs in the browser or node).
I can successfully load my own, local .sf2 or .sf3 files, however
Trying to load an .sfz soundfont throws error 15424120. (And error.message is simply 'undefined'.)
Unlike .sf2 and .sf3, which contain the sounds and instructions in a single file, the .sfz format is just a text instruction file that refers to a separate folder of samples.
The reason I need the .sfz is that I need to be able to edit the .sfz file textually and programatically without an intervening Soundfont generator.
Is there a way to use .sfz's? Do I need to specify Zerberus (the Musescore .sfz player)? Do I need a different file structure? Please see below.
My environment is node js, with the following test case and file structure:
File Structure
Project Folder
app.js
testScore.mscz
mySFZ.sfz
samples
one.wav
two.wav
etc.wav
Test Case (Works with .sf3 , errors with .sfz)
const WebMscore = require('webmscore');
const fs = require('fs');
// free example scores available at https://musescore.com/openscore/scores
const name = 'testScore.mscz';
const exportedPrefix = 'exported';
const filedata = fs.readFileSync(`./${name}`);
WebMscore.ready.then(async () => {
const score = await WebMscore.load('mscz', filedata, [], false);
await score.setSoundFont(fs.readFileSync('./mySFZ.sfz'));
try { fs.writeFileSync(`./${exportedPrefix}.mp3`, await score.saveAudio('mp3')); }
catch (err) { console.log(err) }
score.destroy();
});

Playwright: Upload files from non-input element that cannot be used page.setInputFiles?

I'm working on uploading files through non-input HTML tag on Playwright.
For example, you can use setInputFiles like this, and this works:
await page.setInputFiles('input[type="file"]', './headphone.png')
But apparently setInputFiles only works for input element, something like this will be error:
await page.setInputFiles('label.ImageUpload__label ', './headphone.png');
The HTML I'm working on is like this:
<div id="ImageUpload" class="ImageUpload u-marginB10">
<label class="ImageUpload__label js-dragdrop-area" for="selectFileMultiple">
<span class="ImageUpload__hide">drag and drop or select files</span>
<span class="ImageUpload__text"><span class="js-dragdrop-num">10</span>up to</span>
</label>
</div>
So, is it possible to upload files to such HTML elements with Playwright?
NodeJs: https://playwright.dev/python/docs/api/class-filechooser
page.on("filechooser", (fileChooser: FileChooser) => {
fileChooser.setFiles(["/path/to/a/file"]);
})
Python: https://playwright.dev/python/docs/api/class-filechooser/
with page.expect_file_chooser() as fc_info:
page.click("upload")
file_chooser = fc_info.value
file_chooser.set_files("/path/to/a/file")
Java: https://playwright.dev/java/docs/api/class-filechooser
FileChooser fileChooser = page.waitForFileChooser(() ->
page.click("upload"));
fileChooser.setFiles(Paths.get("myfile.pdf"));
To upload a file using Playwright use setInputFiles(selector, files[, options]) function. This method takes the selector of the input element and the path to the file you want to upload.
The files parameter value can be a relative path (relative to the current working directory) or an absolute path. I strongly suggest that you use an absolute path to ensure predictable behavior.
test("upload a file", async ({ page }) => {
console.log(resolve(__dirname, "bar.png"));
await page.goto("http://127.0.0.1:8080/upload-file/");
await page.locator('input[name="foo"]').click();
await page
.locator('input[name="foo"]')
.setInputFiles(resolve(__dirname, "bar.png"));
await page.click("input[type=submit]");
});
Alternatively, you can read the file into a Buffer and dispatch drop event onto the target element with DataTransfer payload. This is useful when you are testing a drag-and-drop area:
const dataTransfer = await page.evaluateHandle(
async ({ fileHex, localFileName, localFileType }) => {
const dataTransfer = new DataTransfer();
dataTransfer.items.add(
new File([fileHex], localFileName, { type: localFileType })
);
return dataTransfer;
},
{
fileHex: (await readFile(resolve(__dirname, "bar.png"))).toString("hex"),
localFileName: fileName,
localFileType: fileType,
}
);
await page.dispatchEvent("#drop_zone", "drop", { dataTransfer });
await expect(page.locator("text=bar.png")).toBeVisible();
You can further simplify the above code using createDataTransfer utility from playwright-utilities:
const dataTransfer = await createDataTransfer({
page,
filePath: resolve(__dirname, "bar.png"),
fileName: "bar.png",
fileType: "image/png",
});
await page.dispatchEvent("#drop_zone", "drop", { dataTransfer });
await expect(page.locator("text=bar.png")).toBeVisible();
Try this example locally by cloning the Playwright Playground repository:
git clone --branch test/upload-file https://punkpeye#github.com/punkpeye/playwright-playground.git
cd playwright-playground
npm install
npx playwright test tests/upload-file
Found another alternative to upload that worked in my case. We create a buffer from memory and drag and drop the file to the upload button.
// Read your file into a buffer.
const buffer = readFileSync('file.pdf');
// Create the DataTransfer and File
const dataTransfer = await scope.page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data.toString('hex')], 'file.pdf', { type: 'application/pdf' });
dt.items.add(file);
return dt;
}, buffer);
// Now dispatch
await page.dispatchEvent('YOUR_TARGET_SELECTOR', 'drop', { dataTransfer });
if using typescript, add this to the top of the file:
import {readFileSync} from 'fs';
Github issue: https://github.com/microsoft/playwright/issues/10667#issuecomment-998397241
I had the same issue so I decided to use AutoIt to upload files with Playwright.
AutoIt v3 is a freeware BASIC-like scripting language designed for automating Windows GUI and general scripting.
I used AutoIT to handle the Windows File Upload dialog, which cannot be handled using Playwright.
Creating Script
Download AutoIt: https://www.autoitscript.com/site/autoit/downloads/
Open SciTE Script Editor and type the followng:
WinWaitActive("Choose files")
Send("C:\ChromeDriver\text.txt")
Send("{ENTER}")
If it does not work, change Choose files to whatever title is on the top left of the upload dialog.
Click save and name it something like upload.au3 and save it in the root directory of your test.
Example of Save Location
Right click your newly created file and click Compile Script
Executing the script in your test
Create execFile function of child process modules in node.js. Reference: https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback
Add this to the top of your .spec.ts test file:
var exec = require('child_process').execFile;
var upload_script = function(){
exec('upload.exe', function(err, data) {
console.log(err)
});
}
Open the upload dialog, then call the function in your test
// Click Browse
await page.locator('#browse').click();
// Execute Upload Script
upload_script();
You have to run your test headed or it will not work:
npx playwright test --headed

node-canvas registerFont can't find font file once deployed (works locally)

I have a Node.js server that uses node-canvas to render text on an image on the server-side. Here is the repo: https://github.com/shawninder/meme-generator (just git clone, npm i and npm run dev to run locally).
As you'll notice in the code, I am loading the Anton font, which I got from here with the documented registerFont function provided by node-canvas
registerFont('./fonts/Anton-Regular.ttf', { family: 'Anton' })
Everything works like a charm locally, but when I deploy to Vercel (formerly known as zeit), that line throws an ENOENT error:
no such file or directory, lstat '/var/task/fonts'
Is there a path I can use here that will successfully load the font from within a Vercel function?
Can I find a single path that will work both locally and once deployed?
I had the same problem recently and I finally found a solution. I'm no guru, so someone will probably be able to suggest a better way, but here's what worked for me.
Because of how Vercel runs their serverless functions, a function doesn't really know anything about the rest of the project, or the public folder. This makes sense (because security), but it does make it tricky when you need the actual path to a file. You can import the font file no problem, the build process will give it a new name and put it on the disk (in /var/task ), but you can't access it. path.resolve(_font_name_) can see it, but you can't access it.
I ended up writing a very bad, separate api page that used path.join and fs.readdirSync to see what files are actually visible from the api page. One thing that is visible is a node_modules folder that contains the files for modules used on that api page.
fs.readdirSync(path.join(process.cwd(), 'node_modules/')
So what I did was write a local module, install it in my project, then import it into my api page. In the local module's package.json, I have a line "files": ["*"] so it will bundle all the module files into its node_modules folder (instead of just the .js files). In my module I have my font file and a function that copies it to /tmp (/tmp is readable and writable) then returns the path to the file, /tmp/Roboto-Regular.ttf.
On my api page, I include this module, then run it, and I pass the resultant path to registerfont.
It works. I'd share my code, but it's pretty sloppy right now, and I'd like to clean it up and try a couple things first (like I'm not sure if I need to copy it to /tmp, but I haven't tested it without that step). When I get it straightened out I'll edit this answer.
-- EDIT
Since I haven't been able to improve on my original solution, let me give some more details about what I did.
In my package.json I added a line to include a local module:
"dependencies": {
"canvas": "^2.6.1",
"fonttrick": "file:fonttrick",
In my project root, I have a folder "fonttrick". Inside the folder is another package.json:
{
"name": "fonttrick",
"version": "1.0.6",
"description": "a trick to get canvas registerfont to work in a Vercel serverless function",
"license": "MIT",
"homepage": "https://grumbly.games",
"main": "index.js",
"files": [
"*"
],
"keywords": [
"registerfont",
"canvas",
"vercel",
"zeit",
"nextjs"
]
}
This is the only local module I've ever had to write; the keywords don't do anything, but at first I'd thought about putting it on NPM, so they're there.
The fonttrick folder also contains my font file (in this case "Roboto-Regular.ttf"), and a the main file, index.js:
module.exports = function fonttrick() {
const fs = require('fs')
const path = require('path')
const RobotoR = require.resolve('./Roboto-Regular.ttf')
const { COPYFILE_EXCL } = fs.constants;
const { COPYFILE_FICLONE } = fs.constants;
//const pathToRoboto = path.join(process.cwd(), 'node_modules/fonttrick/Roboto-Regular.ttf')
try {
if (fs.existsSync('/tmp/Roboto-Regular.ttf')) {
console.log("Roboto lives in tmp!!!!")
} else {
fs.copyFileSync(RobotoR, '/tmp/Roboto-Regular.ttf', COPYFILE_FICLONE | COPYFILE_EXCL)
}
} catch (err) {
console.error(err)
}
return '/tmp/Roboto-Regular.ttf'
};
I ran npm install in this folder, and then fonttrick was available as a module in my main project (don't forget to run npm install there, too).
Since I only need to use this for API calls, the module is only used in one file, /pages/api/[img].js
import { drawCanvas } from "../../components/drawCanvas"
import { stringIsValid, strToGameState } from '../../components/gameStatePack'
import fonttrick from 'fonttrick'
export default (req, res) => { // { query: { img } }
// some constants
const fallbackString = "1xThe~2ysent~3zlink~4yis~5wnot~6xa~7xvalid~8zsentence~9f~~"
// const fbs64 = Buffer.from(fallbackString,'utf8').toString('base64')
// some variables
let imageWidth = 1200 // standard for fb ogimage
let imageHeight = 628 // standard for fb ogimage
// we need to remove the initial "/api/" before we can use the req string
const reqString64 = req.url.split('/')[2]
// and also it's base64 encoded, so convert to utf8
const reqString = Buffer.from(reqString64, 'base64').toString('utf8')
//const pathToRoboto = path.join(process.cwd(), 'node_modules/fonttrick/Roboto-Regular.ttf')
let output = null
if (stringIsValid({ sentenceString: reqString })) {
let data = JSON.parse(strToGameState({ canvasURLstring: reqString }))
output = drawCanvas({
sentence: data.sentence,
cards: data.cards,
width: imageWidth,
height: imageHeight,
fontPath: fonttrick()
})
} else {
let data = JSON.parse(strToGameState({ canvasURLstring: fallbackString }))
output = drawCanvas({
sentence: data.sentence,
cards: data.cards,
width: imageWidth,
height: imageHeight,
fontPath: fonttrick()
})
}
const buffy = Buffer.from(output.split(',')[1], 'base64')
res.statusCode = 200
res.setHeader('Content-Type', 'image/png')
res.end(buffy)
}
The important part of what this does is import fonttrick which puts a copy of the font in tmp, then returns the path to that file; the path to the font is then passed to the canvas drawing function (along with some other stuff; what to draw, how big to draw it, etc.)
My drawing function itself is in components/drawCanvas.js; here's the important stuff at the beginning (TLDR version: if it gets called from the API page, it gets a path to the font; if so, it uses that, otherwise the regular system fonts are available):
import { registerFont, createCanvas } from 'canvas';
import path from 'path'
// width and height are optional
export const drawCanvas = ({ sentence, cards, width, height, fontPath }) => {
// default canvas size
let cw = 1200 // canvas width
let ch = 628 // canvas height
// if given different canvas size, update
if (width && !height) {
cw = width
ch = Math.floor(width / 1.91)
}
if (height && width) {
cw = width
ch = height
}
if (height && !width) {
ch = height
cw = Math.floor(height * 1.91)
}
// this path is only used for api calls in development mode
let theFontPath = path.join(process.cwd(), 'public/fonts/Roboto-Regular.ttf')
// when run in browser, registerfont isn't available,
// but we don't need it; when run from an API call,
// there is no css loaded, so we can't get fonts from #fontface
// and the canvas element has no fonts installed by default;
// in dev mode we can load them from local, but when run serverless
// it gets complicated: basically, we have a local module whose only
// job is to get loaded and piggyback the font file into the serverless
// function (thread); the module default function copies the font to
// /tmp then returns its absolute path; the function in the api
// then passes that path here so we can load the font from it
if (registerFont !== undefined) {
if (process.env.NODE_ENV === "production") {
theFontPath = fontPath
}
registerFont(theFontPath, { family: 'Roboto' })
}
const canvas = createCanvas(cw, ch)
const ctx = canvas.getContext('2d')
This API path gets used in the header for my game, in the meta tags to create the image on demand when a page gets shared on facebook or twitter or wherever:
<meta property="og:image" content={`https://grumbly.games/api/${returnString}`} />
Anyway. Ugly and hacky, but it works for me.
I think you were very close with registerFont. Here’s what I got to work using your repo:
In img.js:
import { registerFont, createCanvas, loadImage } from 'canvas'
// …
// Where 'Anton' is the same font-family name you want to use within
// your canvas code, ie. in writeText.js.
registerFont('./pages/fonts/Anton/Anton-Regular.ttf', { family: 'Anton' })
// Make sure this goes after registerFont()
const canvas = createCanvas()
//…
I added a new folder in pages/ called fonts/, and added the Anton folder downloaded from Google Fonts. Click “Download Family” to get the font file from here: https://fonts.google.com/specimen/Anton?query=Anton&selection.family=Anton&sidebar.open
The other file you downloaded (https://fonts.googleapis.com/css?family=Anton&display=swap) is actually the CSS file you’ll want to use the fonts client side in the browser, for your previewer.
At first, I would keep using the hosted version provided by Google Fonts. You can add that to the PreviewMeme.js component:
<link href="https://fonts.googleapis.com/css2?family=Anton" rel="stylesheet" />
<canvas id='meme' ref={canvas}></canvas>
(You might also want to use something like FontFaceObserver client side to make sure the font has loaded before rendering your canvas the first time.)
In writeText.js you’ll also then change the fontFamily to Anton:
const fontFamily = 'Anton'
That will make Anton available client side via the hosted Google Fonts, and it should be available to you as a file on the server for rendering with the server-side canvas package.
Hope that’s helpful!
The solution ended up being
import path from 'path'
registerFont(path.resolve('./fonts/Anton-Regular.ttf'), { family: 'Anton' })`
See path.resolve
I finally got this working, using officially-documented configurations rather than the hacky top answer!
First of all, I'm assuming your serverless function is at api/some_function.js, where the api/ folder is at the project root.
Create a folder in api/ to put static files into, such as api/_files/. For me, I put font and image files.
Put this in vercel.json:
{
"functions": {
"api/some_function.js": {
"includeFiles": "_files/**"
}
}
}
Now in api/some_function.js, you can use __dirname to reference the files:
const { join } = require('path')
registerFont(join(__dirname, '_files/fonts/Anton-Regular.ttf'), { family: 'Anton' })
This is based on this Vercel help page, except I had to figure out where the _files/ folder goes in your project directory structure because they forgot to mention that.

fs.createReadStream getting a different path than what's being passed in

I'm using NodeJS on a VM. One part of it serves up pages, and another part is an API. I've run into a problem, where fs.createReadStream attempts to access a different path than what is being passed into the function. I made a small test server to see if it was something else in the server affecting path usage, for whatever reason, but it's happening on my test server as well. First, here's the code:
const fs = require('fs');
const path = require('path');
const csv = require('csv-parser');
const readCSV = (filename) => {
console.log('READ CSV GOT ' + filename); // show me what you got
return new Promise((resolve, reject) => {
const arr = [];
fs.createReadStream(filename)
.pipe(csv())
.on('data', row => {
arr.push(row);
})
.on('error', err => {
console.log(err);
})
.on('end', () => {
resolve(arr);
});
}
}
// tried this:
// const dir = path.relative(
// path.join('path', 'to', 'this', 'file),
// path.join('path', 'to', 'CONTENT.csv')
// );
// tried a literal relative path:
// const dir = '../data/CONTENT.csv';
// tried a literal absolute path:
// const dir = '/repo/directory/server/data/CONTENT.csv';
// tried an absolute path:
const dir = path.join(__dirname, 'data', 'CONTENT.csv');
const content = readCSV(dir)
.then(result => {console.log(result[0]);})
.catch(err => {console.log(err);});
...but any way I slice it, I get the following output:
READCSV GOT /repo/directory/server/data/CONTENT.csv
throw er; // Unhandled 'error' event
^
Error: ENOENT: no such file or directory, open '/repo/directory/data/CONTENT.csv'
i.e., is fs.createReadStream somehow stripping out the directory of the server, for some reason? I suppose I could hard code the directory into the call to createReadStream, maybe? I just want to know why this is happening.
Some extra: I'm stuck on node v8.11, can't go any higher. On the server itself, I believe I'm using older function(param) {...} instead of arrow functions -- but the behavior is exactly the same.
Please help!!
Code is perfect working.
I think you file CONTENT.csv should be in data folder like "/repo/directory/data/CONTENT.csv".
I'm answering my own question, because I found an answer, I'm not entirely sure why it's working, and at least it's interesting. To the best of my estimation, it's got something to do with the call stack, and where NodeJS identifies as the origin of the function call. I've got my server set up in an MVC pattern so my main app.js is in the root dir, and the function that's being called is in /controllers folder, and I've been trying to do relative paths from that folder -- I'm still not sure why absolute paths didn't work.
The call stack goes:
app.js:
app.use('/somepath', endpointRouter);
...then in endpointRouter.js:
router.get('/request/file', endpointController.getFile);
...then finally in endpointController.js:
const readCSV = filename => {
//the code I shared
}
exports.getFile = (req, res, next) => {
// code that calls readCSV(filename)
}
...and I believe that because Node views the chain as originating from app.js, it then treats all relative paths as relative to app.js, in my root folder. Basically when I switched to the super unintuitive single-dot-relative path: './data/CONTENT.csv', it worked with no issue.

Resources