mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Json/Webp export
This commit is contained in:
44
package-lock.json
generated
44
package-lock.json
generated
@ -23,12 +23,13 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-rest-client": "^3.1.1",
|
||||
"open": "^8.4.0",
|
||||
"piexifjs": "^1.0.6",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"webp-converter": "^2.3.3",
|
||||
"webp-converter": "2.3.2",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"bin": {
|
||||
@ -1581,6 +1582,11 @@
|
||||
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
|
||||
"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": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
|
||||
@ -2028,14 +2034,6 @@
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@ -2050,12 +2048,9 @@
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/webp-converter": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.3.tgz",
|
||||
"integrity": "sha512-2p4XvPCIQ/CbUztEFA9vdkILVrRTdMtMxFpQTxlnPc3qx14MV5wnpVvK7m6pG70QdeL+Ser0+Tp843ONwh8VbQ==",
|
||||
"dependencies": {
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.2.tgz",
|
||||
"integrity": "sha512-9kQ9Q/MPzUV2mye8Tv7vA6vDIPk77rI4AWWm2vSaCyGAEsxqyVZYeVU2MSJY5fLkf6u7G5K343vLxKubOxz16Q=="
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
@ -3305,6 +3300,11 @@
|
||||
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
|
||||
"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": {
|
||||
"version": "4.0.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@ -3681,12 +3676,9 @@
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"webp-converter": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.3.tgz",
|
||||
"integrity": "sha512-2p4XvPCIQ/CbUztEFA9vdkILVrRTdMtMxFpQTxlnPc3qx14MV5wnpVvK7m6pG70QdeL+Ser0+Tp843ONwh8VbQ==",
|
||||
"requires": {
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.2.tgz",
|
||||
"integrity": "sha512-9kQ9Q/MPzUV2mye8Tv7vA6vDIPk77rI4AWWm2vSaCyGAEsxqyVZYeVU2MSJY5fLkf6u7G5K343vLxKubOxz16Q=="
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
|
@ -15,12 +15,13 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-rest-client": "^3.1.1",
|
||||
"open": "^8.4.0",
|
||||
"piexifjs": "^1.0.6",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"webp-converter": "^2.3.3",
|
||||
"webp-converter": "2.3.2",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"name": "TavernAI",
|
||||
|
@ -1172,6 +1172,11 @@
|
||||
<div id="advanced_div" class="menu_button" title="Advanced Definitions">
|
||||
<img alt="" class="svg_icon" src="img/book-solid.svg">
|
||||
</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">
|
||||
<img alt="" class="svg_icon" src="img/file-export-solid.svg">
|
||||
</div>
|
||||
|
@ -182,6 +182,9 @@ let is_mes_reload_avatar = false;
|
||||
let optionsPopper = Popper.createPopper(document.getElementById('send_form'), document.getElementById('options'), {
|
||||
placement: 'top-start'
|
||||
});
|
||||
let exportPopper = Popper.createPopper(document.getElementById('export_button'), document.getElementById('export_format_popup'), {
|
||||
placement: 'left',
|
||||
});
|
||||
let dialogueResolve = null;
|
||||
let chat_metadata = {};
|
||||
|
||||
@ -4482,12 +4485,41 @@ $(document).ready(function () {
|
||||
},
|
||||
});
|
||||
});
|
||||
$("#export_button").click(function () {
|
||||
var link = document.createElement("a");
|
||||
link.href = "characters/" + characters[this_chid].avatar;
|
||||
link.download = characters[this_chid].avatar;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
$("#export_button").click(function (e) {
|
||||
$('#export_format_popup').toggle();
|
||||
exportPopper.update();
|
||||
});
|
||||
$(document).on('click', '.export_format', async function () {
|
||||
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_button").click(function () {
|
||||
@ -4589,6 +4621,10 @@ $(document).ready(function () {
|
||||
$("html").on('touchstart mousedown', function (e) {
|
||||
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'];
|
||||
for (const id of forbiddenTargets) {
|
||||
if (clickTarget.closest(id).length > 0) {
|
||||
@ -4607,7 +4643,6 @@ $(document).ready(function () {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -18,32 +18,3 @@
|
||||
#roll_dice:hover {
|
||||
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;
|
||||
}
|
@ -2576,6 +2576,38 @@ a {
|
||||
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 */
|
||||
|
61
server.js
61
server.js
@ -26,6 +26,7 @@ const ipaddr = require('ipaddr.js');
|
||||
const json5 = require('json5');
|
||||
|
||||
const ExifReader = require('exifreader');
|
||||
const exif = require('piexifjs');
|
||||
const webp = require('webp-converter');
|
||||
|
||||
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) {
|
||||
//console.log(humanizedISO8601DateTime()+':/importchat begun');
|
||||
if (!request.body) return response.sendStatus(400);
|
||||
|
Reference in New Issue
Block a user