diff --git a/package-lock.json b/package-lock.json index f75ca0d31..4a435ec2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,11 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "csrf-csrf": "^2.2.3", + "exifreader": "^4.12.0", "express": "^4.18.2", "ipaddr.js": "^2.0.1", "jimp": "^0.22.7", + "json5": "^2.2.3", "mime-types": "^2.1.35", "multer": "^1.4.5-lts.1", "node-rest-client": "^3.1.1", @@ -26,6 +28,7 @@ "png-chunks-extract": "^1.0.0", "rimraf": "^3.0.2", "sanitize-filename": "^1.6.3", + "webp-converter": "^2.3.3", "ws": "^8.13.0" }, "bin": { @@ -450,6 +453,15 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" }, + "node_modules/@xmldom/xmldom": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.10.tgz", + "integrity": "sha512-hb9QhOg5MGmpVkFcoZ9XJMe1em5gd0e2eqqjK87O1dwULedXsnY/Zg/Ju6lcohA+t6jVkmKpe7I1etqhvdRdrQ==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -887,6 +899,15 @@ "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" }, + "node_modules/exifreader": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/exifreader/-/exifreader-4.12.0.tgz", + "integrity": "sha512-aRSmNyw2c6f6qPK4jmC56W/5XePDN7LVwt8tQjgMchxoY3MCxqEToegirKdS7A3CYCWAOPehfypMZWGWxtLhzw==", + "hasInstallScript": true, + "optionalDependencies": { + "@xmldom/xmldom": "^0.7.8" + } + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -1252,6 +1273,17 @@ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/load-bmfont": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", @@ -1996,6 +2028,14 @@ "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", @@ -2009,6 +2049,14 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "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" + } + }, "node_modules/whatwg-fetch": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", @@ -2405,6 +2453,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" }, + "@xmldom/xmldom": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.10.tgz", + "integrity": "sha512-hb9QhOg5MGmpVkFcoZ9XJMe1em5gd0e2eqqjK87O1dwULedXsnY/Zg/Ju6lcohA+t6jVkmKpe7I1etqhvdRdrQ==", + "optional": true + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2738,6 +2792,14 @@ "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" }, + "exifreader": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/exifreader/-/exifreader-4.12.0.tgz", + "integrity": "sha512-aRSmNyw2c6f6qPK4jmC56W/5XePDN7LVwt8tQjgMchxoY3MCxqEToegirKdS7A3CYCWAOPehfypMZWGWxtLhzw==", + "requires": { + "@xmldom/xmldom": "^0.7.8" + } + }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -3019,6 +3081,11 @@ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, "load-bmfont": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", @@ -3598,6 +3665,11 @@ "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", @@ -3608,6 +3680,14 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "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" + } + }, "whatwg-fetch": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", diff --git a/package.json b/package.json index 41f2b25da..48668408f 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,15 @@ "dependencies": { "@dqbd/tiktoken": "^1.0.2", "axios": "^1.3.4", + "compression": "^1", "cookie-parser": "^1.4.6", - "compression":"^1", "cors": "^2.8.5", "csrf-csrf": "^2.2.3", + "exifreader": "^4.12.0", "express": "^4.18.2", "ipaddr.js": "^2.0.1", "jimp": "^0.22.7", + "json5": "^2.2.3", "mime-types": "^2.1.35", "multer": "^1.4.5-lts.1", "node-rest-client": "^3.1.1", @@ -18,6 +20,7 @@ "png-chunks-extract": "^1.0.0", "rimraf": "^3.0.2", "sanitize-filename": "^1.6.3", + "webp-converter": "^2.3.3", "ws": "^8.13.0" }, "name": "TavernAI", diff --git a/public/index.html b/public/index.html index 6cc15661d..90ef8816f 100644 --- a/public/index.html +++ b/public/index.html @@ -258,7 +258,7 @@
+
- +

Name

@@ -1297,7 +1297,7 @@
diff --git a/public/script.js b/public/script.js index 824fa7bce..48da24d84 100644 --- a/public/script.js +++ b/public/script.js @@ -4437,7 +4437,7 @@ $(document).ready(function () { var ext = file.name.match(/\.(\w+)$/); if ( !ext || - (ext[1].toLowerCase() != "json" && ext[1].toLowerCase() != "png") + (ext[1].toLowerCase() != "json" && ext[1].toLowerCase() != "png" && ext[1] != "webp") ) { return; } diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 68e99e98b..cec46d094 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -222,7 +222,11 @@ function RA_checkOnlineStatus() { //Auto-connect to API (when set to kobold, API URL exists, and auto_connect is true) function RA_autoconnect(PrevApi) { - if ((online_status === undefined || online_status === "no_connection") && LoadLocalBool('AutoConnectEnabled')) { + if (online_status === undefined) { + setTimeout(RA_autoconnect, 100); + return; + } + if (online_status === "no_connection" && LoadLocalBool('AutoConnectEnabled')) { switch (main_api) { case 'kobold': if (api_server && isUrlOrAPIKey(api_server)) { diff --git a/server.js b/server.js index 33fa228ae..4905ddb28 100644 --- a/server.js +++ b/server.js @@ -23,6 +23,10 @@ const mime = require('mime-types'); const cookieParser = require('cookie-parser'); const crypto = require('crypto'); const ipaddr = require('ipaddr.js'); +const json5 = require('json5'); + +const ExifReader = require('exifreader'); +const webp = require('webp-converter'); const config = require(path.join(process.cwd(), './config.conf')); const server_port = process.env.SILLY_TAVERN_PORT || config.port; @@ -395,7 +399,7 @@ app.post("/getchat", jsonParser, function (request, response) { const lines = data.split('\n'); // Iterate through the array of strings and parse each line as JSON - const jsonData = lines.map(JSON.parse); + const jsonData = lines.map(json5.parse); response.send(jsonData); //console.log('read the requested file') @@ -441,7 +445,7 @@ app.post("/getstatus", jsonParser, function (request, response_getstatus = respo var response = body.match(/gradio_config[ =]*(\{.*\});/)[1]; if (!response) throw "no_connection"; - let model = JSON.parse(response).components.filter((x) => x.props.label == "Model" && x.type == "dropdown")[0].props.value; + let model = json5.parse(response).components.filter((x) => x.props.label == "Model" && x.type == "dropdown")[0].props.value; data = { result: model }; if (!data) throw "no_connection"; @@ -678,28 +682,44 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes = } } +async function charaRead(img_url, input_format) { + let format; + if (input_format === undefined) { + if (img_url.indexOf('.webp') !== -1) { + format = 'webp'; + } else { + format = 'png'; + } + } else { + format = input_format; + } + switch (format) { + case 'webp': + const exif_data = await ExifReader.load(fs.readFileSync(img_url)); + const char_data = exif_data['UserComment']['description']; + if (char_data === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) { + return exif_data['UserComment'].value[0]; + } + return char_data; + case 'png': + const buffer = fs.readFileSync(img_url); + const chunks = extract(buffer); - - -function charaRead(img_url) { - const buffer = fs.readFileSync(img_url); - const chunks = extract(buffer); - - const textChunks = chunks.filter(function (chunk) { - return chunk.name === 'tEXt'; - }).map(function (chunk) { - //console.log(text.decode(chunk.data)); - return PNGtext.decode(chunk.data); - }); - var base64DecodedData = Buffer.from(textChunks[0].text, 'base64').toString('utf8'); - return base64DecodedData;//textChunks[0].text; - //console.log(textChunks[0].keyword); // 'hello' - //console.log(textChunks[0].text);    // 'world' + const textChunks = chunks.filter(function (chunk) { + return chunk.name === 'tEXt'; + }).map(function (chunk) { + return PNGtext.decode(chunk.data); + }); + var base64DecodedData = Buffer.from(textChunks[0].text, 'base64').toString('utf8'); + return base64DecodedData;//textChunks[0].text; + default: + break; + } } app.post("/getcharacters", jsonParser, function (request, response) { - fs.readdir(charactersPath, (err, files) => { + fs.readdir(charactersPath, async (err, files) => { if (err) { console.error(err); return; @@ -710,11 +730,10 @@ app.post("/getcharacters", jsonParser, function (request, response) { //console.log(pngFiles); characters = {}; var i = 0; - pngFiles.forEach(item => { - //console.log(item); - var img_data = charaRead(charactersPath + item); + for (const item of pngFiles) { try { - let jsonObject = JSON.parse(img_data); + var img_data = await charaRead(charactersPath + item); + let jsonObject = json5.parse(img_data); jsonObject.avatar = item; //console.log(jsonObject); characters[i] = {}; @@ -727,7 +746,7 @@ app.post("/getcharacters", jsonParser, function (request, response) { console.log("An unexpected error occurred: ", error); } } - }); + }; //console.log(characters); response.send(JSON.stringify(characters)); }); @@ -746,7 +765,7 @@ app.post("/getbackgrounds", jsonParser, function (request, response) { app.post("/iscolab", jsonParser, function (request, response) { let send_data = false; if (is_colab) { - send_data = String(process.env.colaburl).trim(); + send_data = String(process.env.colaburl).trim(); } response.send({ colaburl: send_data }); @@ -1036,7 +1055,7 @@ function readWorldInfoFile(worldInfoName) { } const worldInfoText = fs.readFileSync(pathToWorldInfo, 'utf8'); - const worldInfo = JSON.parse(worldInfoText); + const worldInfo = json5.parse(worldInfoText); return worldInfo; } @@ -1222,7 +1241,7 @@ app.post("/getallchatsofcharacter", jsonParser, function (request, response) { }); rl.on('close', () => { if (lastLine) { - let jsonData = JSON.parse(lastLine); + let jsonData = json5.parse(lastLine); if (jsonData.name !== undefined) { chatData[i] = {}; chatData[i]['file_name'] = file; @@ -1247,6 +1266,7 @@ app.post("/getallchatsofcharacter", jsonParser, function (request, response) { }; }) }); + function getPngName(file) { let i = 1; let base_name = file; @@ -1256,23 +1276,24 @@ function getPngName(file) { } return file; } + app.post("/importcharacter", urlencodedParser, async function (request, response) { if (!request.body) return response.sendStatus(400); let png_name = ''; let filedata = request.file; - //console.log(filedata.filename); + let uploadPath = path.join('./uploads', filedata.filename); var format = request.body.file_type; //console.log(format); if (filedata) { if (format == 'json') { - fs.readFile('./uploads/' + filedata.filename, 'utf8', async (err, data) => { + fs.readFile(uploadPath, 'utf8', async (err, data) => { if (err) { console.log(err); response.send({ error: true }); } - const jsonData = JSON.parse(data); + const jsonData = json5.parse(data); if (jsonData.name !== undefined) { jsonData.name = sanitize(jsonData.name); @@ -1295,43 +1316,29 @@ app.post("/importcharacter", urlencodedParser, async function (request, response }); } else { try { - - var img_data = charaRead('./uploads/' + filedata.filename); - let jsonData = JSON.parse(img_data); + var img_data = await charaRead(uploadPath, format); + let jsonData = json5.parse(img_data); jsonData.name = sanitize(jsonData.name); + if (format == 'webp') { + let convertedPath = path.join('./uploads', path.basename(uploadPath, ".webp") + ".png") + await webp.dwebp(uploadPath, convertedPath, "-o"); + uploadPath = convertedPath; + } + png_name = getPngName(jsonData.name); if (jsonData.name !== undefined) { let char = { "name": jsonData.name, "description": jsonData.description ?? '', "personality": jsonData.personality ?? '', "first_mes": jsonData.first_mes ?? '', "avatar": 'none', "chat": humanizedISO8601DateTime(), "mes_example": jsonData.mes_example ?? '', "scenario": jsonData.scenario ?? '', "create_date": humanizedISO8601DateTime(), "talkativeness": jsonData.talkativeness ?? 0.5 }; char = JSON.stringify(char); - await charaWrite('./uploads/' + filedata.filename, char, png_name, response, { file_name: png_name }); - /* - fs.copyFile('./uploads/'+filedata.filename, charactersPath+png_name+'.png', (err) => { - if(err) { - response.send({error:true}); - return console.log(err); - }else{ - //console.log(img_file+fileType); - response.send({file_name: png_name}); - } - //console.log('The image was copied from temp directory.'); - });*/ + await charaWrite(uploadPath, char, png_name, response, { file_name: png_name }); } } catch (err) { console.log(err); response.send({ error: true }); } } - //charaWrite(img_path+img_file, char, request.body.ch_name, response); } - //console.log("The file was saved."); - - - //console.log(request.body); - //response.send(request.body.ch_name); - - //response.redirect("https://metanit.com") }); app.post("/importchat", urlencodedParser, function (request, response) { @@ -1356,7 +1363,7 @@ app.post("/importchat", urlencodedParser, function (request, response) { response.send({ error: true }); } - const jsonData = JSON.parse(data); + const jsonData = json5.parse(data); var new_chat = []; if (jsonData.histories !== undefined) { //console.log('/importchat confirms JSON histories are defined'); @@ -1409,7 +1416,7 @@ app.post("/importchat", urlencodedParser, function (request, response) { }); rl.once('line', (line) => { - let jsonData = JSON.parse(line); + let jsonData = json5.parse(line); if (jsonData.user_name !== undefined) { //console.log(humanizedISO8601DateTime()+':/importchat copying chat as '+ch_name+' - '+humanizedISO8601DateTime()+'.jsonl'); @@ -1447,7 +1454,7 @@ app.post('/importworldinfo', urlencodedParser, (request, response) => { const fileContents = fs.readFileSync(pathToUpload, 'utf8'); try { - const worldContent = JSON.parse(fileContents); + const worldContent = json5.parse(fileContents); if (!('entries' in worldContent)) { throw new Error('File must contain a world info entries list'); } @@ -1519,7 +1526,7 @@ app.post('/getgroups', jsonParser, (_, response) => { const files = fs.readdirSync(directories.groups); files.forEach(function (file) { const fileContents = fs.readFileSync(path.join(directories.groups, file), 'utf8'); - const group = JSON.parse(fileContents); + const group = json5.parse(fileContents); groups.push(group); }); @@ -1578,7 +1585,7 @@ app.post('/getgroupchat', jsonParser, (request, response) => { const lines = data.split('\n'); // Iterate through the array of strings and parse each line as JSON - const jsonData = lines.map(JSON.parse); + const jsonData = lines.map(json5.parse); return response.send(jsonData); } else { return response.send([]); @@ -1641,7 +1648,7 @@ app.post('/status_poe', jsonParser, async (request, response) => { const botNames = client.get_bot_names(); client.disconnect_ws(); - return response.send({'bot_names': botNames}); + return response.send({ 'bot_names': botNames }); } catch { return response.sendStatus(401); @@ -1661,8 +1668,8 @@ app.post('/purge_poe', jsonParser, async (request, response) => { const client = await getPoeClient(token); await client.purge_conversation(bot, count); client.disconnect_ws(); - - return response.send({"ok" : true}); + + return response.send({ "ok": true }); } catch { return response.sendStatus(500); @@ -1673,24 +1680,24 @@ app.post('/generate_poe', jsonParser, async (request, response) => { if (!request.body.token || !request.body.prompt) { return response.sendStatus(400); } - + const token = request.body.token; const prompt = request.body.prompt; const bot = request.body.bot ?? POE_DEFAULT_BOT; - + try { const client = await getPoeClient(token); - + let reply; for await (const mes of client.send_message(bot, prompt)) { reply = mes.text; } console.log(reply); - + client.disconnect_ws(); - - return response.send({'reply': reply}); + + return response.send({ 'reply': reply }); } catch { return response.sendStatus(500); @@ -2034,8 +2041,6 @@ function convertStage1() { getCharacterFile2(directories, 0); } function convertStage2() { - //directoriesB = JSON.parse(directoriesB); - //console.log(directoriesB); var mes = true; for (const key in directoriesB) { if (mes) { @@ -2044,9 +2049,6 @@ function convertStage2() { console.log('***'); mes = false; } - //console.log(`${key}: ${directoriesB[key]}`); - //console.log(JSON.parse(charactersB[key])); - //console.log(directoriesB[key]); var char = JSON.parse(charactersB[key]); char.create_date = humanizedISO8601DateTime();