Support all file formats for replace/update

1315f0968bbc94aec0b6c3d1d0392cae00130784

Cohee <18619528+Cohee1207@users.noreply.github.com>

3 files changed, +29 -19Ignore whitespace
public/index.html+1 -1
@@ -4796,7 +4796,7 @@
47964796 <input multiple type="file" id="character_import_file" accept=".json, image/png, .yaml, .yml, .charx" name="avatar">
47974797 <input id="character_import_file_type" name="file_type" class="text_pole" maxlength="999" size="2" value="" autocomplete="off">
47984798 </form>
47994799 <input type="file" id="character_replace_file" accept=".json, image/png, .yaml, .yml, .charx" name="replace_avatar" hidden>
48004800 </div>
48014801 <div name="Character List Panel" id="rm_characters_block" class="right_menu">
48024802 <div id="charListFixedTop">
public/script.js+11 -8
@@ -8449,10 +8449,10 @@ async function connectAPISlash(_, text) {
84498449/**
84508450 * Imports supported files dropped into the app window.
84518451 * @param {File[]} files Array of files to process
84528452 * @param {boolean?Map<File, string>} preserveFileNames[data] WhetherExtra data to preservepass originalto filethe namesimport function
84538453 * @returns {Promise<void>}
84548454 */
84558455export async function processDroppedFiles(files, preserveFileNamesdata = falsenew Map()) {
84568456 const allowedMimeTypes = [
84578457 'application/json',
84588458 'image/png',
@@ -8469,7 +8469,8 @@ export async function processDroppedFiles(files, preserveFileNames = false) {
84698469 for (const file of files) {
84708470 const extension = file.name.split('.').pop().toLowerCase();
84718471 if (allowedMimeTypes.includes(file.type) || allowedExtensions.includes(extension)) {
8472- await importCharacter(file, preserveFileNames);
8472+ const preservedName = data instanceof Map && data.get(file);
8473+ await importCharacter(file, preservedName);
84738474 } else {
84748475 toastr.warning('Unsupported file type: ' + file.name);
84758476 }
@@ -8479,10 +8480,10 @@ export async function processDroppedFiles(files, preserveFileNames = false) {
84798480/**
84808481 * Imports a character from a file.
84818482 * @param {File} file File to import
84828483 * @param {booleanstring?} preserveFileName Whether to preserve original file name
84838484 * @returns {Promise<void>}
84848485 */
84858486async function importCharacter(file, preserveFileName = false'') {
84868487 if (is_group_generating || is_send_press) {
84878488 toastr.error('Cannot import characters while generating. Stop the request and try again.', 'Import aborted');
84888489 throw new Error('Cannot import character while generating');
@@ -8498,7 +8499,7 @@ async function importCharacter(file, preserveFileName = false) {
84988499 const formData = new FormData();
84998500 formData.append('avatar', file);
85008501 formData.append('file_type', format);
85018502 if (preserveFileName) formData.append('preserve_file_namepreserved_name', String(preserveFileName));
85028503
85038504 const data = await jQuery.ajax({
85048505 type: 'POST',
@@ -10637,10 +10638,12 @@ jQuery(async function () {
1063710638 }
1063810639
1063910640 try {
10640- const cloneFile = new File([file], characters[this_chid].avatar, { type: file.type });
1064110641 const chatFile = characters[this_chid]['chat'];
10642- await processDroppedFiles([cloneFile], true);
10642+ const data = new Map();
10643+ data.set(file, characters[this_chid].avatar);
10644+ await processDroppedFiles([file], data);
1064310645 await openCharacterChat(chatFile);
10646+ await fetch(getThumbnailUrl('avatar', characters[this_chid].avatar), { cache: 'no-cache' });
1064410647 } catch {
1064510648 toastr.error('Failed to replace the character card.', 'Something went wrong');
1064610649 }
src/endpoints/characters.js+17 -10
@@ -499,15 +499,16 @@ function convertWorldInfoToCharacterBook(name, entries) {
499499 * Import a character from a YAML file.
500500 * @param {string} uploadPath Path to the uploaded file
501501 * @param {{ request: import('express').Request, response: import('express').Response }} context Express request and response objects
502+ * @param {string|undefined} preservedFileName Preserved file name
502503 * @returns {Promise<string>} Internal name of the character
503504 */
504505async function importFromYaml(uploadPath, context, preservedFileName) {
505506 const fileText = fs.readFileSync(uploadPath, 'utf8');
506507 fs.rmSync(uploadPath);
507508 const yamlData = yaml.parse(fileText);
508509 console.log('Importing from YAML');
509510 yamlData.name = sanitize(yamlData.name);
510511 const fileName = preservedFileName || getPngName(yamlData.name, context.request.user.directories);
511512 let char = convertToV2({
512513 'name': yamlData.name,
513514 'description': yamlData.context ?? '',
@@ -532,9 +533,10 @@ async function importFromYaml(uploadPath, context) {
532533 * @param {string} uploadPath
533534 * @param {object} params
534535 * @param {import('express').Request} params.request
536+ * @param {string|undefined} preservedFileName Preserved file name
535537 * @returns {Promise<string>} Internal name of the character
536538 */
537539async function importFromCharX(uploadPath, { request }, preservedFileName) {
538540 const data = fs.readFileSync(uploadPath);
539541 fs.rmSync(uploadPath);
540542 console.log('Importing from CharX');
@@ -567,7 +569,7 @@ async function importFromCharX(uploadPath, { request }) {
567569 unsetFavFlag(card);
568570 card['create_date'] = humanizedISO8601DateTime();
569571 card.name = sanitize(card.name);
570572 const fileName = preservedFileName || getPngName(card.name, request.user.directories);
571573 const result = await writeCharacterData(avatar, JSON.stringify(card), fileName, request);
572574 return result ? fileName : '';
573575}
@@ -576,9 +578,10 @@ async function importFromCharX(uploadPath, { request }) {
576578 * Import a character from a JSON file.
577579 * @param {string} uploadPath Path to the uploaded file
578580 * @param {{ request: import('express').Request, response: import('express').Response }} context Express request and response objects
581+ * @param {string|undefined} preservedFileName Preserved file name
579582 * @returns {Promise<string>} Internal name of the character
580583 */
581584async function importFromJson(uploadPath, { request }, preservedFileName) {
582585 const data = fs.readFileSync(uploadPath, 'utf8');
583586 fs.unlinkSync(uploadPath);
584587
@@ -590,7 +593,7 @@ async function importFromJson(uploadPath, { request }) {
590593 unsetFavFlag(jsonData);
591594 jsonData = readFromV2(jsonData);
592595 jsonData['create_date'] = humanizedISO8601DateTime();
593596 const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories);
594597 const char = JSON.stringify(jsonData);
595598 const result = await writeCharacterData(defaultAvatarPath, char, pngName, request);
596599 return result ? pngName : '';
@@ -600,7 +603,7 @@ async function importFromJson(uploadPath, { request }) {
600603 if (jsonData.creator_notes) {
601604 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
602605 }
603606 const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories);
604607 let char = {
605608 'name': jsonData.name,
606609 'description': jsonData.description ?? '',
@@ -626,7 +629,7 @@ async function importFromJson(uploadPath, { request }) {
626629 if (jsonData.creator_notes) {
627630 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
628631 }
629632 const pngName = preservedFileName || getPngName(jsonData.char_name, request.user.directories);
630633 let char = {
631634 'name': jsonData.char_name,
632635 'description': jsonData.char_persona ?? '',
@@ -1089,8 +1092,8 @@ function getPngName(file, directories) {
10891092 * @returns {string | undefined} - The preserved name if the request is valid, otherwise undefined
10901093 */
10911094function getPreservedName(request) {
10921095 return typeof request.body.file_typepreserved_name === 'pngstring' && request.body.preserve_file_name === 'true'preserved_name.length &&> request.file?.originalname0
10931096 ? path.parse(request.filebody.originalnamepreserved_name).name
10941097 : undefined;
10951098}
10961099
@@ -1123,6 +1126,10 @@ router.post('/import', urlencodedParser, async function (request, response) {
11231126 return response.sendStatus(400);
11241127 }
11251128
1129+ if (preservedFileName) {
1130+ invalidateThumbnail(request.user.directories, 'avatar', `${preservedFileName}.png`);
1131+ }
1132+
11261133 response.send({ file_name: fileName });
11271134 } catch (err) {
11281135 console.log(err);