(WIP) Import JSON from .charx

This commit is contained in:
Cohee 2024-06-02 00:28:41 +03:00
parent a41fe1d801
commit 20d12dc98e
4 changed files with 42 additions and 5 deletions

View File

@ -4788,7 +4788,7 @@
</div>
<div id="rm_character_import" class="right_menu" style="display: none;">
<form id="form_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">
<input multiple type="file" id="character_import_file" accept=".json, image/png, .yaml, .yml" name="avatar">
<input multiple type="file" id="character_import_file" accept=".json, image/png, .yaml, .yml, .charx" name="avatar">
<input id="character_import_file_type" name="file_type" class="text_pole" maxlength="999" size="2" value="" autocomplete="off">
</form>
<input type="file" id="character_replace_file" accept="image/png" name="replace_avatar" hidden>

View File

@ -8288,8 +8288,13 @@ export async function processDroppedFiles(files, preserveFileNames = false) {
'text/x-yaml',
];
const allowedExtensions = [
'charx',
];
for (const file of files) {
if (allowedMimeTypes.includes(file.type)) {
const extension = file.name.split('.').pop().toLowerCase();
if (allowedMimeTypes.includes(file.type) || allowedExtensions.includes(extension)) {
await importCharacter(file, preserveFileNames);
} else {
toastr.warning('Unsupported file type: ' + file.name);
@ -8310,7 +8315,7 @@ async function importCharacter(file, preserveFileName = false) {
}
const ext = file.name.match(/\.(\w+)$/);
if (!ext || !(['json', 'png', 'yaml', 'yml'].includes(ext[1].toLowerCase()))) {
if (!ext || !(['json', 'png', 'yaml', 'yml', 'charx'].includes(ext[1].toLowerCase()))) {
return;
}

View File

@ -13,7 +13,7 @@ const jimp = require('jimp');
const { UPLOADS_PATH, AVATAR_WIDTH, AVATAR_HEIGHT } = require('../constants');
const { jsonParser, urlencodedParser } = require('../express-common');
const { deepMerge, humanizedISO8601DateTime, tryParse } = require('../util');
const { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer } = require('../util');
const { TavernCardValidator } = require('../validator/TavernCardValidator');
const characterCardParser = require('../character-card-parser.js');
const { readWorldInfoFile } = require('./worldinfo');
@ -486,6 +486,37 @@ async function importFromYaml(uploadPath, context) {
return result ? fileName : '';
}
/**
* Imports a character card from CharX (ZIP) file.
* @param {string} uploadPath
* @param {object} params
* @param {import('express').Request} params.request
* @returns {Promise<string>} Internal name of the character
*/
async function importFromCharX(uploadPath, { request }) {
const data = fs.readFileSync(uploadPath);
fs.rmSync(uploadPath);
console.log('Importing from CharX');
const cardBuffer = await extractFileFromZipBuffer(data, 'card.json');
if (!cardBuffer) {
throw new Error('Failed to extract card.json from CharX file');
}
const card = readFromV2(JSON.parse(cardBuffer.toString()));
if (card.spec === undefined) {
throw new Error('Invalid CharX card file: missing spec field');
}
unsetFavFlag(card);
card['create_date'] = humanizedISO8601DateTime();
card.name = sanitize(card.name);
const fileName = getPngName(card.name, request.user.directories);
const result = await writeCharacterData(defaultAvatarPath, JSON.stringify(card), fileName, request);
return result ? fileName : '';
}
/**
* Import a character from a JSON file.
* @param {string} uploadPath Path to the uploaded file
@ -1016,6 +1047,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
'yml': importFromYaml,
'json': importFromJson,
'png': importFromPng,
'charx': importFromCharX,
};
try {

View File

@ -149,7 +149,7 @@ async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
zipfile.readEntry();
zipfile.on('entry', (entry) => {
if (entry.fileName.endsWith(fileExtension)) {
if (entry.fileName.endsWith(fileExtension) && !entry.fileName.startsWith('__MACOSX')) {
console.log(`Extracting ${entry.fileName}`);
zipfile.openReadStream(entry, (err, readStream) => {
if (err) {