Json/Webp export

This commit is contained in:
SillyLossy
2023-04-08 18:09:53 +03:00
parent a3a32e9d64
commit 2c57d0efb6
7 changed files with 160 additions and 63 deletions

44
package-lock.json generated
View File

@ -23,12 +23,13 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-rest-client": "^3.1.1", "node-rest-client": "^3.1.1",
"open": "^8.4.0", "open": "^8.4.0",
"piexifjs": "^1.0.6",
"png-chunk-text": "^1.0.0", "png-chunk-text": "^1.0.0",
"png-chunks-encode": "^1.0.0", "png-chunks-encode": "^1.0.0",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"webp-converter": "^2.3.3", "webp-converter": "2.3.2",
"ws": "^8.13.0" "ws": "^8.13.0"
}, },
"bin": { "bin": {
@ -1581,6 +1582,11 @@
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
"integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
}, },
"node_modules/piexifjs": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/piexifjs/-/piexifjs-1.0.6.tgz",
"integrity": "sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag=="
},
"node_modules/pixelmatch": { "node_modules/pixelmatch": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
@ -2028,14 +2034,6 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -2050,12 +2048,9 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
}, },
"node_modules/webp-converter": { "node_modules/webp-converter": {
"version": "2.3.3", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.3.tgz", "resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.2.tgz",
"integrity": "sha512-2p4XvPCIQ/CbUztEFA9vdkILVrRTdMtMxFpQTxlnPc3qx14MV5wnpVvK7m6pG70QdeL+Ser0+Tp843ONwh8VbQ==", "integrity": "sha512-9kQ9Q/MPzUV2mye8Tv7vA6vDIPk77rI4AWWm2vSaCyGAEsxqyVZYeVU2MSJY5fLkf6u7G5K343vLxKubOxz16Q=="
"dependencies": {
"uuid": "^8.3.2"
}
}, },
"node_modules/whatwg-fetch": { "node_modules/whatwg-fetch": {
"version": "3.6.2", "version": "3.6.2",
@ -3305,6 +3300,11 @@
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
"integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
}, },
"piexifjs": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/piexifjs/-/piexifjs-1.0.6.tgz",
"integrity": "sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag=="
},
"pixelmatch": { "pixelmatch": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
@ -3665,11 +3665,6 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
}, },
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -3681,12 +3676,9 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
}, },
"webp-converter": { "webp-converter": {
"version": "2.3.3", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.3.tgz", "resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.2.tgz",
"integrity": "sha512-2p4XvPCIQ/CbUztEFA9vdkILVrRTdMtMxFpQTxlnPc3qx14MV5wnpVvK7m6pG70QdeL+Ser0+Tp843ONwh8VbQ==", "integrity": "sha512-9kQ9Q/MPzUV2mye8Tv7vA6vDIPk77rI4AWWm2vSaCyGAEsxqyVZYeVU2MSJY5fLkf6u7G5K343vLxKubOxz16Q=="
"requires": {
"uuid": "^8.3.2"
}
}, },
"whatwg-fetch": { "whatwg-fetch": {
"version": "3.6.2", "version": "3.6.2",

View File

@ -15,12 +15,13 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-rest-client": "^3.1.1", "node-rest-client": "^3.1.1",
"open": "^8.4.0", "open": "^8.4.0",
"piexifjs": "^1.0.6",
"png-chunk-text": "^1.0.0", "png-chunk-text": "^1.0.0",
"png-chunks-encode": "^1.0.0", "png-chunks-encode": "^1.0.0",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"webp-converter": "^2.3.3", "webp-converter": "2.3.2",
"ws": "^8.13.0" "ws": "^8.13.0"
}, },
"name": "TavernAI", "name": "TavernAI",

View File

@ -1172,6 +1172,11 @@
<div id="advanced_div" class="menu_button" title="Advanced Definitions"> <div id="advanced_div" class="menu_button" title="Advanced Definitions">
<img alt="" class="svg_icon" src="img/book-solid.svg"> <img alt="" class="svg_icon" src="img/book-solid.svg">
</div> </div>
<div id="export_format_popup" class="list-group">
<div class="export_format list-group-item" data-format="png">PNG</div>
<div class="export_format list-group-item" data-format="json">JSON</div>
<div class="export_format list-group-item" data-format="webp">WEBP</div>
</div>
<div id="export_button" class="menu_button" title="Export and Download"> <div id="export_button" class="menu_button" title="Export and Download">
<img alt="" class="svg_icon" src="img/file-export-solid.svg"> <img alt="" class="svg_icon" src="img/file-export-solid.svg">
</div> </div>

View File

@ -182,6 +182,9 @@ let is_mes_reload_avatar = false;
let optionsPopper = Popper.createPopper(document.getElementById('send_form'), document.getElementById('options'), { let optionsPopper = Popper.createPopper(document.getElementById('send_form'), document.getElementById('options'), {
placement: 'top-start' placement: 'top-start'
}); });
let exportPopper = Popper.createPopper(document.getElementById('export_button'), document.getElementById('export_format_popup'), {
placement: 'left',
});
let dialogueResolve = null; let dialogueResolve = null;
let chat_metadata = {}; let chat_metadata = {};
@ -4482,12 +4485,41 @@ $(document).ready(function () {
}, },
}); });
}); });
$("#export_button").click(function () { $("#export_button").click(function (e) {
var link = document.createElement("a"); $('#export_format_popup').toggle();
link.href = "characters/" + characters[this_chid].avatar; exportPopper.update();
link.download = characters[this_chid].avatar; });
document.body.appendChild(link); $(document).on('click', '.export_format', async function () {
link.click(); const format = $(this).data('format');
if (!format) {
return;
}
const body = { format, avatar_url: characters[this_chid].avatar };
const response = await fetch('/exportcharacter', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token,
},
body: JSON.stringify(body),
});
if (response.ok) {
const filename = characters[this_chid].avatar.replace('.png', `.${format}`);
const blob = await response.blob();
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.setAttribute("download", filename);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
$('#export_format_popup').hide();
}); });
//**************************CHAT IMPORT EXPORT*************************// //**************************CHAT IMPORT EXPORT*************************//
$("#chat_import_button").click(function () { $("#chat_import_button").click(function () {
@ -4589,6 +4621,10 @@ $(document).ready(function () {
$("html").on('touchstart mousedown', function (e) { $("html").on('touchstart mousedown', function (e) {
var clickTarget = $(e.target); var clickTarget = $(e.target);
if ($('#export_format_popup').is(':visible') && clickTarget.closest('#export_button').length == 0 && clickTarget.closest('#export_format_popup').length == 0) {
$('#export_format_popup').hide();
}
const forbiddenTargets = ['#character_cross', '#avatar-and-name-block', '#shadow_popup', '#world_popup']; const forbiddenTargets = ['#character_cross', '#avatar-and-name-block', '#shadow_popup', '#world_popup'];
for (const id of forbiddenTargets) { for (const id of forbiddenTargets) {
if (clickTarget.closest(id).length > 0) { if (clickTarget.closest(id).length > 0) {
@ -4607,7 +4643,6 @@ $(document).ready(function () {
} }
} }
} }
}); });

View File

@ -18,32 +18,3 @@
#roll_dice:hover { #roll_dice:hover {
opacity: 1; opacity: 1;
} }
.list-group {
display: flex;
flex-direction: column;
padding-left: 0;
margin-top: 0;
margin-bottom: 3px;
overflow: hidden;
background-color: black;
border: 1px solid #666;
border-radius: 15px;
box-shadow: 0 0 5px black;
text-shadow: 0 0 3px black;
}
.list-group-item:hover {
background-color: rgba(255, 255, 255, 0.3);
}
.list-group-item {
color: rgba(229, 224, 216, 1);
position: relative;
display: block;
padding: 0.75rem 1.25rem;
margin-bottom: -1px;
box-sizing: border-box;
user-select: none;
cursor: pointer;
}

View File

@ -2576,6 +2576,38 @@ a {
text-decoration: none; text-decoration: none;
} }
#export_format_popup {
display: none;
}
.list-group {
display: flex;
flex-direction: column;
padding-left: 0;
margin-top: 0;
margin-bottom: 3px;
overflow: hidden;
background-color: black;
border: 1px solid #666;
border-radius: 15px;
box-shadow: 0 0 5px black;
text-shadow: 0 0 3px black;
}
.list-group-item:hover {
background-color: rgba(255, 255, 255, 0.3);
}
.list-group-item {
color: rgba(229, 224, 216, 1);
position: relative;
display: block;
padding: 0.75rem 1.25rem;
margin-bottom: -1px;
box-sizing: border-box;
user-select: none;
cursor: pointer;
}
/* ############################################################# */ /* ############################################################# */
/* Right nav panel and nav-toggle */ /* Right nav panel and nav-toggle */

View File

@ -26,6 +26,7 @@ const ipaddr = require('ipaddr.js');
const json5 = require('json5'); const json5 = require('json5');
const ExifReader = require('exifreader'); const ExifReader = require('exifreader');
const exif = require('piexifjs');
const webp = require('webp-converter'); const webp = require('webp-converter');
const config = require(path.join(process.cwd(), './config.conf')); const config = require(path.join(process.cwd(), './config.conf'));
@ -1341,6 +1342,66 @@ app.post("/importcharacter", urlencodedParser, async function (request, response
} }
}); });
app.post("/exportcharacter", jsonParser, async function (request, response) {
if (!request.body.format || !request.body.avatar_url) {
return response.sendStatus(400);
}
let filename = path.join(directories.characters, sanitize(request.body.avatar_url));
if (!fs.existsSync(filename)) {
return response.sendStatus(404);
}
switch (request.body.format) {
case 'png':
return response.sendFile(filename, { root: __dirname });
case 'json': {
try {
let json = await charaRead(filename);
let jsonObject = json5.parse(json);
return response.type('json').send(jsonObject)
}
catch {
return response.sendStatus(400);
}
}
case 'webp': {
try {
let json = await charaRead(filename);
let inputWebpPath = `./uploads/${Date.now()}_input.webp`;
let outputWebpPath = `./uploads/${Date.now()}_output.webp`;
let metadataPath = `./uploads/${Date.now()}_metadata.exif`;
let metadata =
{
"Exif": {
[exif.ExifIFD.UserComment]: json,
},
};
const exifString = exif.dump(metadata);
fs.writeFileSync(metadataPath, exifString, 'binary');
await webp.cwebp(filename, inputWebpPath, '-q 95');
await webp.webpmux_add(inputWebpPath, outputWebpPath, metadataPath, 'exif');
response.sendFile(outputWebpPath, { root: __dirname });
fs.rmSync(inputWebpPath);
fs.rmSync(metadataPath);
return;
}
catch (err) {
console.log(err);
return response.sendStatus(400);
}
}
}
return response.sendStatus(400);
});
app.post("/importchat", urlencodedParser, function (request, response) { app.post("/importchat", urlencodedParser, function (request, response) {
//console.log(humanizedISO8601DateTime()+':/importchat begun'); //console.log(humanizedISO8601DateTime()+':/importchat begun');
if (!request.body) return response.sendStatus(400); if (!request.body) return response.sendStatus(400);