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",
|
"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",
|
||||||
|
@ -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",
|
||||||
|
@ -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>
|
||||||
|
@ -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 () {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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 */
|
||||||
|
61
server.js
61
server.js
@ -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);
|
||||||
|
Reference in New Issue
Block a user