Skip to content

Instantly share code, notes, and snippets.

@tripzilch
Last active October 22, 2023 15:51
Show Gist options
  • Save tripzilch/ca327ee3f74716fe3528782c604bc196 to your computer and use it in GitHub Desktop.
Save tripzilch/ca327ee3f74716fe3528782c604bc196 to your computer and use it in GitHub Desktop.
/*
very simple local PNG/SVG saving server by Piter Pasma
USAGE:
it needs a folder ./saved/ and ./saved/frames/ in the
folder where you're running the script. command line:
node save-server.js
there are no options, but you can easily modify the code
here's a sample save_frame function:
-----
PROJ_NAME = "dimensionless-void";
function save_frame(ctx, n) {
ctx.canvas.toBlob(blob => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(`OK SAVE ${n} -- ${xhr.responseText}`);
// optionally put call to render the next frame here
// or you can do location.reload or something
}
}
// note that the saving behaviour is different depending on whether you pass
// in the `frame` parameter. just try it out, or see the code below for how it works.
xhr.open('POST', `http://127.0.0.1:8080/save?desc=${PROJ_NAME}&frame=${n}`);
xhr.send(blob); // alternatively you can also use a local IP like 192.168.178.23:8080
});
}
*/
var http = require('http');
var fs = require('fs');
function dateFormat (date, fstr, utc=false) {
// from https://stackoverflow.com/a/10647272
utc = utc ? 'getUTC' : 'get';
return fstr.replace (/%[YmdHMS]/g, function (m) {
switch (m) {
case '%Y': return date[utc + 'FullYear'] (); // no leading zeros required
case '%m': m = 1 + date[utc + 'Month'] (); break;
case '%d': m = date[utc + 'Date'] (); break;
case '%H': m = date[utc + 'Hours'] (); break;
case '%M': m = date[utc + 'Minutes'] (); break;
case '%S': m = date[utc + 'Seconds'] (); break;
default: return m.slice (1); // unknown code, remove %
}
// add leading zero if required
return ('0' + m).slice (-2);
});
}
const clean = s => s.replace(/[^0-9A-Za-z]+/g, '-');
console.log('START SERVER! OKAY');
http.createServer(function (req, res) {
const url = new URL(req.url, `http://${req.headers.host}`);
const desc = url.searchParams.get('desc');
const frame = url.searchParams.get('frame');
if (url.pathname == '/save' && req.method === 'POST') {
const content_type = req.headers['content-type'];
console.log(`====== SAVE, content-type=${content_type}`);
const chunks = [];
req.on('data', data => {chunks.push(data); });
req.on('end', () => {
let data = Buffer.concat(chunks);
console.log(`=== Received data, length=${data.length}`);
let ext = false;
if (content_type === 'image/png') {
ext = 'png';
} else if (content_type === 'text/plain;charset=UTF-8') {
data = data.toString();
const data_svg_header0 = 'data:image/svg+xml,';
const data_svg_header1 = 'data:image/svg+xml;charset=utf-8,';
// const data_png_header = 'data:image/png;base64,';
if (data.startsWith(data_svg_header0)) {
data = data.substring(data_svg_header0.length);
data = decodeURIComponent(data);
ext = 'svg';
} else if (data.startsWith(data_svg_header1)) {
data = data.substring(data_svg_header1.length);
data = decodeURIComponent(data);
ext = 'svg';
} /*else if (data.startsWith(data_png_header)) {
data = data.substring(data_png_header.length);
data = atob(data); // this not work?
ext = 'png';
}*/
}
if (!ext) {
res.writeHead(200, {'Content-Type': 'text/html', 'Access-Control-Allow-Origin': '*'});
console.log(`Unexpected data, content_type = ${content_type}`);
console.log(data.substr(0, 250));
res.write("Error: Unexpected data.");
return res.end();
}
const fn_desc = desc ? clean(desc) : 'untitled';
const date = dateFormat(new Date(), '%Y-%m-%d-%H-%M-%S');
let filename;
if (frame) {
filename = `./saved/frames/${fn_desc}-${clean(frame)}.${ext}`;
} else {
filename = `./saved/${fn_desc}-${date}.${ext}`;
}
fs.writeFile(filename, data, (err) => {
if (err) throw err;
console.log("File saved to " + filename);
res.writeHead(200, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin': '*'
});
res.write(`OK! ${data.length} bytes written to ${filename}`);
return res.end();
});
});
} else if (req.method === 'OPTIONS') {
console.log(`OPTIONS!`);
res.writeHead(204, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Method': 'OPTIONS, POST',
'Access-Control-Allow-Headers': 'Content-Type'
});
return res.end();
} else {
console.log('REQ 404!', req);
res.writeHead(404, {'Content-Type': 'text/html', 'Access-Control-Allow-Origin': '*'});
res.write('Error 404');
return res.end();
}
}).listen(8080);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment