New validation logic of expression images

This commit is contained in:
SillyLossy
2023-04-20 20:26:32 +03:00
parent 1a3616ae47
commit 59284a5436
2 changed files with 138 additions and 59 deletions

View File

@ -1,4 +1,4 @@
import { saveSettingsDebounced } from "../../../script.js";
import { saveSettingsDebounced, token } from "../../../script.js";
import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js";
export { MODULE_NAME };
@ -38,7 +38,7 @@ const DEFAULT_EXPRESSIONS = [
let expressionsList = null;
let lastCharacter = undefined;
let lastMessage = null;
let existingExpressions = [];
let spriteCache = {};
let inApiCall = false;
function onExpressionsShowDefaultInput() {
@ -58,21 +58,25 @@ function onExpressionsShowDefaultInput() {
}
}
let isWorkerBusy = false;
async function moduleWorkerWrapper() {
// Don't touch me I'm busy...
if (isWorkerBusy) {
return;
}
// I'm free. Let's update!
try {
isWorkerBusy = true;
await moduleWorker();
}
finally {
isWorkerBusy = false;
}
}
async function moduleWorker() {
function getLastCharacterMessage() {
const reversedChat = context.chat.slice().reverse();
for (let mes of reversedChat) {
if (mes.is_user || mes.is_system) {
continue;
}
return { mes: mes.mes, name: mes.name };
}
return { mes: '', name: null };
}
const context = getContext();
// non-characters not supported
@ -84,27 +88,34 @@ async function moduleWorker() {
// character changed
if (context.groupId !== lastCharacter && context.characterId !== lastCharacter) {
removeExpression();
validateImages();
spriteCache = {};
}
const currentLastMessage = getLastCharacterMessage();
// character has no expressions or it is not loaded
if (Object.keys(spriteCache).length === 0) {
await validateImages(currentLastMessage.name);
lastCharacter = context.groupId || context.characterId;
}
if (!modules.includes('classify')) {
$('.expression_settings').show();
$('.expression_settings .offline_mode').css('display', 'block');
lastCharacter = context.characterId;
lastCharacter = context.groupId || context.characterId;
if (context.groupId) {
await validateImages(currentLastMessage.name, true);
}
return;
}
else {
$('.expression_settings .offline_mode').css('display', 'none');
}
// character has no expressions or it is not loaded
if (!context.groupId && existingExpressions.length === 0) {
lastCharacter = context.groupId || context.characterId;
return;
}
// check if last message changed
const currentLastMessage = getLastCharacterMessage();
if ((lastCharacter === context.characterId || lastCharacter === context.groupId)
&& lastMessage === currentLastMessage.mes) {
return;
@ -154,48 +165,67 @@ async function moduleWorker() {
}
}
function getLastCharacterMessage() {
const context = getContext();
const reversedChat = context.chat.slice().reverse();
for (let mes of reversedChat) {
if (mes.is_user || mes.is_system) {
continue;
}
return { mes: mes.mes, name: mes.name };
}
return { mes: '', name: null };
}
function removeExpression() {
lastMessage = null;
$('img.expression').off('error');
$('img.expression').prop('src', '');
$('img.expression').removeClass('default');
$('.expression_settings').hide();
}
let imagesValidating = false;
async function validateImages() {
if (imagesValidating) {
async function validateImages(character, forceRedrawCached) {
if (!character) {
return;
}
imagesValidating = true;
existingExpressions = [];
const context = getContext();
const labels = await getExpressionsList();
if (spriteCache[character]) {
if (forceRedrawCached && $('#image_list').data('name') !== character) {
console.log('force redrawing character sprites list')
drawSpritesList(character, labels, spriteCache[character]);
}
return;
}
const sprites = await getSpritesList(character);
let validExpressions = drawSpritesList(character, labels, sprites);
spriteCache[character] = validExpressions;
}
function drawSpritesList(character, labels, sprites) {
let validExpressions = [];
$('.expression_settings').show();
$('#image_list').empty();
$('#image_list').data('name', character);
labels.sort().forEach((item) => {
const sprite = sprites.find(x => x.label == item);
if (!context.characterId) {
imagesValidating = false;
return;
if (sprite) {
validExpressions.push(sprite);
$('#image_list').append(getListItem(item, sprite.path, 'success'));
}
const IMAGE_LIST = (await getExpressionsList()).map(x => ({ name: x, file: `${x}.png` }));
IMAGE_LIST.forEach((item) => {
const image = document.createElement('img');
image.src = `/characters/${context.name2}/${item.file}`;
image.classList.add('debug-image');
image.width = '0px';
image.height = '0px';
image.onload = function () {
existingExpressions.push(item.name);
$('#image_list').append(getListItem(item.file, image.src, 'success'));
else {
$('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure'));
}
image.onerror = function () {
$('#image_list').append(getListItem(item.file, '/img/No-Image-Placeholder.svg', 'failure'));
}
$('#image_list').prepend(image);
});
imagesValidating = false;
return validExpressions;
}
function getListItem(item, imageSrc, textClass) {
@ -207,9 +237,28 @@ function getListItem(item, imageSrc, textClass) {
`;
}
async function getSpritesList(name) {
console.log('getting sprites list');
try {
const result = await fetch(`/get_sprites?name=${encodeURIComponent(name)}`, {
headers: {
'X-CSRF-Token': token,
}
});
let sprites = result.ok ? (await result.json()) : [];
return sprites;
}
catch (err) {
console.log(err);
return [];
}
}
async function getExpressionsList() {
console.log('getting expressions list');
// get something for offline mode (6 default images)
// get something for offline mode (default images)
if (!modules.includes('classify')) {
console.log('classify not available, loading default');
return DEFAULT_EXPRESSIONS;
@ -246,13 +295,14 @@ async function getExpressionsList() {
async function setExpression(character, expression, force) {
console.log('entered setExpressions');
const filename = `${expression}.png`;
await validateImages(character);
const img = $('img.expression');
const sprite = (spriteCache[character] && spriteCache[character].find(x => x.label === expression));
console.log('checking for expression images to show..');
if (force || (existingExpressions.includes(expression))) {
if (sprite) {
console.log('setting expression from character images folder');
const imgUrl = `/characters/${character}/${filename}`;
img.attr('src', imgUrl);
img.attr('src', sprite.path);
img.removeClass('default');
img.off('error');
img.on('error', function () {
@ -270,7 +320,7 @@ async function setExpression(character, expression, force) {
function setDefault() {
console.log('setting default');
const defImgUrl = `/img/default-expressions/${filename}`;
const defImgUrl = `/img/default-expressions/${expression}.png`;
//console.log(defImgUrl);
img.attr('src', defImgUrl);
img.addClass('default');
@ -284,11 +334,11 @@ function onClickExpressionImage() {
return;
}
const context = getContext();
const expression = $(this).attr('id').replace('.png', '');
const expression = $(this).attr('id');
const name = getLastCharacterMessage().name;
if ($(this).find('.failure').length === 0) {
setExpression(context.name2, expression, true);
setExpression(name, expression, true);
}
}
@ -329,5 +379,5 @@ function onClickExpressionImage() {
addExpressionImage();
addSettings();
setInterval(moduleWorker, UPDATE_INTERVAL);
setInterval(moduleWorkerWrapper, UPDATE_INTERVAL);
})();

View File

@ -1943,6 +1943,35 @@ app.get('/discover_extensions', jsonParser, function (_, response) {
return response.send(extensions);
});
app.get('/get_sprites', jsonParser, function (request, response) {
const name = request.query.name;
const spritesPath = path.join(directories.characters, name);
let sprites = [];
try {
if (fs.existsSync(spritesPath) && fs.statSync(spritesPath).isDirectory()) {
sprites = fs.readdirSync(spritesPath)
.filter(file => {
const mimeType = mime.lookup(file);
return mimeType && mimeType.startsWith('image/');
})
.map((file) => {
const pathToSprite = path.join(spritesPath, file);
return {
label: path.parse(pathToSprite).name.toLowerCase(),
path: `/characters/${name}/${file}`,
};
});
}
}
catch (err) {
console.log(err);
}
finally {
return response.send(sprites);
}
});
function getThumbnailFolder(type) {
let thumbnailFolder;