Upload or delete a sprite image.

This commit is contained in:
SillyLossy
2023-05-29 01:17:52 +03:00
parent 5efd55d343
commit 602bceb26e
3 changed files with 685 additions and 509 deletions

View File

@ -1,385 +1,470 @@
import { saveSettingsDebounced } from "../../../script.js"; import { callPopup, getRequestHeaders, saveSettingsDebounced } from "../../../script.js";
import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js"; import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js";
export { MODULE_NAME }; export { MODULE_NAME };
const MODULE_NAME = 'expressions'; const MODULE_NAME = 'expressions';
const UPDATE_INTERVAL = 2000; const UPDATE_INTERVAL = 2000;
const DEFAULT_EXPRESSIONS = [ const DEFAULT_EXPRESSIONS = [
"admiration", "admiration",
"amusement", "amusement",
"anger", "anger",
"annoyance", "annoyance",
"approval", "approval",
"caring", "caring",
"confusion", "confusion",
"curiosity", "curiosity",
"desire", "desire",
"disappointment", "disappointment",
"disapproval", "disapproval",
"disgust", "disgust",
"embarrassment", "embarrassment",
"excitement", "excitement",
"fear", "fear",
"gratitude", "gratitude",
"grief", "grief",
"joy", "joy",
"love", "love",
"nervousness", "nervousness",
"optimism", "optimism",
"pride", "pride",
"realization", "realization",
"relief", "relief",
"remorse", "remorse",
"sadness", "sadness",
"surprise", "surprise",
"neutral" "neutral"
]; ];
let expressionsList = null; let expressionsList = null;
let lastCharacter = undefined; let lastCharacter = undefined;
let lastMessage = null; let lastMessage = null;
let spriteCache = {}; let spriteCache = {};
let inApiCall = false; let inApiCall = false;
function onExpressionsShowDefaultInput() { function onExpressionsShowDefaultInput() {
const value = $(this).prop('checked'); const value = $(this).prop('checked');
extension_settings.expressions.showDefault = value; extension_settings.expressions.showDefault = value;
saveSettingsDebounced(); saveSettingsDebounced();
const existingImageSrc = $('img.expression').prop('src'); const existingImageSrc = $('img.expression').prop('src');
if (existingImageSrc !== undefined) { //if we have an image in src if (existingImageSrc !== undefined) { //if we have an image in src
if (!value && existingImageSrc.includes('/img/default-expressions/')) { //and that image is from /img/ (default) if (!value && existingImageSrc.includes('/img/default-expressions/')) { //and that image is from /img/ (default)
$('img.expression').prop('src', ''); //remove it $('img.expression').prop('src', ''); //remove it
lastMessage = null; lastMessage = null;
} }
if (value) { if (value) {
lastMessage = null; lastMessage = null;
} }
} }
} }
let isWorkerBusy = false; let isWorkerBusy = false;
async function moduleWorkerWrapper() { async function moduleWorkerWrapper() {
// Don't touch me I'm busy... // Don't touch me I'm busy...
if (isWorkerBusy) { if (isWorkerBusy) {
return; return;
} }
// I'm free. Let's update! // I'm free. Let's update!
try { try {
isWorkerBusy = true; isWorkerBusy = true;
await moduleWorker(); await moduleWorker();
} }
finally { finally {
isWorkerBusy = false; isWorkerBusy = false;
} }
} }
async function moduleWorker() { async function moduleWorker() {
const context = getContext(); const context = getContext();
// non-characters not supported // non-characters not supported
if (!context.groupId && context.characterId === undefined) { if (!context.groupId && context.characterId === undefined) {
removeExpression(); removeExpression();
return; return;
} }
// character changed // character changed
if (context.groupId !== lastCharacter && context.characterId !== lastCharacter) { if (context.groupId !== lastCharacter && context.characterId !== lastCharacter) {
removeExpression(); removeExpression();
spriteCache = {}; spriteCache = {};
} }
const currentLastMessage = getLastCharacterMessage(); const currentLastMessage = getLastCharacterMessage();
// character has no expressions or it is not loaded // character has no expressions or it is not loaded
if (Object.keys(spriteCache).length === 0) { if (Object.keys(spriteCache).length === 0) {
await validateImages(currentLastMessage.name); await validateImages(currentLastMessage.name);
lastCharacter = context.groupId || context.characterId; lastCharacter = context.groupId || context.characterId;
} }
const offlineMode = $('.expression_settings .offline_mode'); const offlineMode = $('.expression_settings .offline_mode');
if (!modules.includes('classify')) { if (!modules.includes('classify')) {
$('.expression_settings').show(); $('.expression_settings').show();
offlineMode.css('display', 'block'); offlineMode.css('display', 'block');
lastCharacter = context.groupId || context.characterId; lastCharacter = context.groupId || context.characterId;
if (context.groupId) { if (context.groupId) {
await validateImages(currentLastMessage.name, true); await validateImages(currentLastMessage.name, true);
} }
return; return;
} }
else { else {
// force reload expressions list on connect to API // force reload expressions list on connect to API
if (offlineMode.is(':visible')) { if (offlineMode.is(':visible')) {
expressionsList = null; expressionsList = null;
spriteCache = {}; spriteCache = {};
expressionsList = await getExpressionsList(); expressionsList = await getExpressionsList();
await validateImages(currentLastMessage.name, true); await validateImages(currentLastMessage.name, true);
} }
offlineMode.css('display', 'none'); offlineMode.css('display', 'none');
} }
// check if last message changed // check if last message changed
if ((lastCharacter === context.characterId || lastCharacter === context.groupId) if ((lastCharacter === context.characterId || lastCharacter === context.groupId)
&& lastMessage === currentLastMessage.mes) { && lastMessage === currentLastMessage.mes) {
return; return;
} }
// API is busy // API is busy
if (inApiCall) { if (inApiCall) {
return; return;
} }
try { try {
inApiCall = true; inApiCall = true;
const url = new URL(getApiUrl()); const url = new URL(getApiUrl());
url.pathname = '/api/classify'; url.pathname = '/api/classify';
const apiResult = await fetch(url, { const apiResult = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Bypass-Tunnel-Reminder': 'bypass', 'Bypass-Tunnel-Reminder': 'bypass',
}, },
body: JSON.stringify({ text: currentLastMessage.mes }) body: JSON.stringify({ text: currentLastMessage.mes })
}); });
if (apiResult.ok) { if (apiResult.ok) {
const name = context.groupId ? currentLastMessage.name : context.name2; const name = context.groupId ? currentLastMessage.name : context.name2;
const force = !!context.groupId; const force = !!context.groupId;
const data = await apiResult.json(); const data = await apiResult.json();
let expression = data.classification[0].label; let expression = data.classification[0].label;
// Character won't be angry on you for swiping // Character won't be angry on you for swiping
if (currentLastMessage.mes == '...' && expressionsList.includes('joy')) { if (currentLastMessage.mes == '...' && expressionsList.includes('joy')) {
expression = 'joy'; expression = 'joy';
} }
setExpression(name, expression, force); setExpression(name, expression, force);
} }
} }
catch (error) { catch (error) {
console.log(error); console.log(error);
} }
finally { finally {
inApiCall = false; inApiCall = false;
lastCharacter = context.groupId || context.characterId; lastCharacter = context.groupId || context.characterId;
lastMessage = currentLastMessage.mes; lastMessage = currentLastMessage.mes;
} }
} }
function getLastCharacterMessage() { function getLastCharacterMessage() {
const context = getContext(); const context = getContext();
const reversedChat = context.chat.slice().reverse(); const reversedChat = context.chat.slice().reverse();
for (let mes of reversedChat) { for (let mes of reversedChat) {
if (mes.is_user || mes.is_system) { if (mes.is_user || mes.is_system) {
continue; continue;
} }
return { mes: mes.mes, name: mes.name }; return { mes: mes.mes, name: mes.name };
} }
return { mes: '', name: null }; return { mes: '', name: null };
} }
function removeExpression() { function removeExpression() {
lastMessage = null; lastMessage = null;
$('img.expression').off('error'); $('img.expression').off('error');
$('img.expression').prop('src', ''); $('img.expression').prop('src', '');
$('img.expression').removeClass('default'); $('img.expression').removeClass('default');
$('.expression_settings').hide(); $('.expression_settings').hide();
} }
async function validateImages(character, forceRedrawCached) { async function validateImages(character, forceRedrawCached) {
if (!character) { if (!character) {
return; return;
} }
const labels = await getExpressionsList(); const labels = await getExpressionsList();
if (spriteCache[character]) { if (spriteCache[character]) {
if (forceRedrawCached && $('#image_list').data('name') !== character) { if (forceRedrawCached && $('#image_list').data('name') !== character) {
console.log('force redrawing character sprites list') console.log('force redrawing character sprites list')
drawSpritesList(character, labels, spriteCache[character]); drawSpritesList(character, labels, spriteCache[character]);
} }
return; return;
} }
const sprites = await getSpritesList(character); const sprites = await getSpritesList(character);
let validExpressions = drawSpritesList(character, labels, sprites); let validExpressions = drawSpritesList(character, labels, sprites);
spriteCache[character] = validExpressions; spriteCache[character] = validExpressions;
} }
function drawSpritesList(character, labels, sprites) { function drawSpritesList(character, labels, sprites) {
let validExpressions = []; let validExpressions = [];
$('.expression_settings').show(); $('.expression_settings').show();
$('#image_list').empty(); $('#image_list').empty();
$('#image_list').data('name', character); $('#image_list').data('name', character);
labels.sort().forEach((item) => { labels.sort().forEach((item) => {
const sprite = sprites.find(x => x.label == item); const sprite = sprites.find(x => x.label == item);
if (sprite) { if (sprite) {
validExpressions.push(sprite); validExpressions.push(sprite);
$('#image_list').append(getListItem(item, sprite.path, 'success')); $('#image_list').append(getListItem(item, sprite.path, 'success'));
} }
else { else {
$('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure')); $('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure'));
} }
}); });
return validExpressions; return validExpressions;
} }
function getListItem(item, imageSrc, textClass) { function getListItem(item, imageSrc, textClass) {
return ` return `
<div id="${item}" class="expression_list_item"> <div id="${item}" class="expression_list_item">
<span class="expression_list_title ${textClass}">${item}</span> <div class="expression_list_buttons">
<img class="expression_list_image" src="${imageSrc}" /> <div class="menu_button expression_list_upload" title="Upload image">
</div> <i class="fa-solid fa-upload"></i>
`; </div>
} <div class="menu_button expression_list_delete" title="Delete image">
<i class="fa-solid fa-trash"></i>
async function getSpritesList(name) { </div>
console.log('getting sprites list'); </div>
<span class="expression_list_title ${textClass}">${item}</span>
try { <img class="expression_list_image" src="${imageSrc}" />
const result = await fetch(`/get_sprites?name=${encodeURIComponent(name)}`); </div>
`;
let sprites = result.ok ? (await result.json()) : []; }
return sprites;
} async function getSpritesList(name) {
catch (err) { console.log('getting sprites list');
console.log(err);
return []; try {
} const result = await fetch(`/get_sprites?name=${encodeURIComponent(name)}`);
}
let sprites = result.ok ? (await result.json()) : [];
async function getExpressionsList() { return sprites;
// get something for offline mode (default images) }
if (!modules.includes('classify')) { catch (err) {
return DEFAULT_EXPRESSIONS; console.log(err);
} return [];
}
if (Array.isArray(expressionsList)) { }
return expressionsList;
} async function getExpressionsList() {
// get something for offline mode (default images)
const url = new URL(getApiUrl()); if (!modules.includes('classify')) {
url.pathname = '/api/classify/labels'; return DEFAULT_EXPRESSIONS;
}
try {
const apiResult = await fetch(url, { if (Array.isArray(expressionsList)) {
method: 'GET', return expressionsList;
headers: { 'Bypass-Tunnel-Reminder': 'bypass' }, }
});
const url = new URL(getApiUrl());
if (apiResult.ok) { url.pathname = '/api/classify/labels';
const data = await apiResult.json(); try {
expressionsList = data.labels; const apiResult = await fetch(url, {
return expressionsList; method: 'GET',
} headers: { 'Bypass-Tunnel-Reminder': 'bypass' },
} });
catch (error) {
console.log(error); if (apiResult.ok) {
return [];
} const data = await apiResult.json();
} expressionsList = data.labels;
return expressionsList;
async function setExpression(character, expression, force) { }
console.log('entered setExpressions'); }
await validateImages(character); catch (error) {
const img = $('img.expression'); console.log(error);
return [];
const sprite = (spriteCache[character] && spriteCache[character].find(x => x.label === expression)); }
console.log('checking for expression images to show..'); }
if (sprite) {
console.log('setting expression from character images folder'); async function setExpression(character, expression, force) {
img.attr('src', sprite.path); console.log('entered setExpressions');
img.removeClass('default'); await validateImages(character);
img.off('error'); const img = $('img.expression');
img.on('error', function () {
$(this).attr('src', ''); const sprite = (spriteCache[character] && spriteCache[character].find(x => x.label === expression));
if (force && extension_settings.expressions.showDefault) { console.log('checking for expression images to show..');
setDefault(); if (sprite) {
} console.log('setting expression from character images folder');
}); img.attr('src', sprite.path);
} else { img.removeClass('default');
if (extension_settings.expressions.showDefault) { img.off('error');
setDefault(); img.on('error', function () {
} $(this).attr('src', '');
} if (force && extension_settings.expressions.showDefault) {
setDefault();
function setDefault() { }
console.log('setting default'); });
const defImgUrl = `/img/default-expressions/${expression}.png`; } else {
//console.log(defImgUrl); if (extension_settings.expressions.showDefault) {
img.attr('src', defImgUrl); setDefault();
img.addClass('default'); }
} }
document.getElementById("expression-holder").style.display = '';
} function setDefault() {
console.log('setting default');
function onClickExpressionImage() { const defImgUrl = `/img/default-expressions/${expression}.png`;
// online mode doesn't need force set //console.log(defImgUrl);
if (modules.includes('classify')) { img.attr('src', defImgUrl);
return; img.addClass('default');
} }
document.getElementById("expression-holder").style.display = '';
const expression = $(this).attr('id'); }
const name = getLastCharacterMessage().name;
function onClickExpressionImage() {
if ($(this).find('.failure').length === 0) { // online mode doesn't need force set
setExpression(name, expression, true); if (modules.includes('classify')) {
} return;
} }
(function () { const expression = $(this).attr('id');
function addExpressionImage() { const name = getLastCharacterMessage().name;
const html = `
<div id="expression-wrapper"> if ($(this).find('.failure').length === 0) {
<div id="expression-holder" class="expression-holder" style="display:none;"> setExpression(name, expression, true);
<div id="expression-holderheader" class="fa-solid fa-grip drag-grabber"></div> }
<img id="expression-image" class="expression"> }
</div> async function onClickExpressionUpload(event) {
</div>`; // Prevents the expression from being set
$('body').append(html); event.stopPropagation();
}
function addSettings() { const id = $(this).closest('.expression_list_item').attr('id');
const name = $('#image_list').data('name');
const html = `
<div class="expression_settings"> const handleExpressionUploadChange = async (e) => {
<div class="inline-drawer"> const file = e.target.files[0];
<div class="inline-drawer-toggle inline-drawer-header">
<b>Expression images</b> if (!file) {
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> return;
</div> }
<div class="inline-drawer-content">
<p class="offline_mode">You are in offline mode. Click on the image below to set the expression.</p> const formData = new FormData();
<div id="image_list"></div> formData.append('name', name);
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character. formData.append('label', id);
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p> formData.append('avatar', file);
<label for="expressions_show_default"><input id="expressions_show_default" type="checkbox">Show default images (emojis) if missing</label>
</div> try {
</div> await jQuery.ajax({
</div> type: "POST",
`; url: "/upload_sprite",
$('#extensions_settings').append(html); data: formData,
$('#expressions_show_default').on('input', onExpressionsShowDefaultInput); beforeSend: function () { },
$('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input'); cache: false,
$(document).on('click', '.expression_list_item', onClickExpressionImage); contentType: false,
$('.expression_settings').hide(); processData: false,
} });
addExpressionImage(); // Refresh sprites list
addSettings(); delete spriteCache[name];
setInterval(moduleWorkerWrapper, UPDATE_INTERVAL); await validateImages(name);
moduleWorkerWrapper(); } catch (error) {
})(); toastr.error('Failed to upload image');
}
// Reset the input
e.target.form.reset();
};
$('#expression_upload')
.off('change')
.on('change', handleExpressionUploadChange)
.trigger('click');
}
async function onClickExpressionDelete(event) {
// Prevents the expression from being set
event.stopPropagation();
const confirmation = await callPopup("<h3>Are you sure?</h3>Once deleted, it's gone forever!", 'confirm');
if (!confirmation) {
return;
}
const id = $(this).closest('.expression_list_item').attr('id');
const name = $('#image_list').data('name');
try {
await fetch('/delete_sprite', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ name, label: id }),
});
} catch (error) {
toastr.error('Failed to delete image. Try again later.');
}
// Refresh sprites list
delete spriteCache[name];
await validateImages(name);
}
(function () {
function addExpressionImage() {
const html = `
<div id="expression-wrapper">
<div id="expression-holder" class="expression-holder" style="display:none;">
<div id="expression-holderheader" class="fa-solid fa-grip drag-grabber"></div>
<img id="expression-image" class="expression">
</div>
</div>`;
$('body').append(html);
}
function addSettings() {
const html = `
<div class="expression_settings">
<div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header">
<b>Expression images</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<p class="offline_mode">You are in offline mode. Click on the image below to set the expression.</p>
<div id="image_list"></div>
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character.
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
<label for="expressions_show_default"><input id="expressions_show_default" type="checkbox">Show default images (emojis) if missing</label>
</div>
</div>
<form><input type="file" id="expression_upload" name="expression_upload" accept="image/*" hidden></form>
</div>
`;
$('#extensions_settings').append(html);
$('#expressions_show_default').on('input', onExpressionsShowDefaultInput);
$('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input');
$(document).on('click', '.expression_list_item', onClickExpressionImage);
$(document).on('click', '.expression_list_upload', onClickExpressionUpload);
$(document).on('click', '.expression_list_delete', onClickExpressionDelete);
$('.expression_settings').hide();
}
addExpressionImage();
addSettings();
setInterval(moduleWorkerWrapper, UPDATE_INTERVAL);
moduleWorkerWrapper();
})();

View File

@ -1,124 +1,138 @@
.expression-helper { .expression-helper {
display: inline-block; display: inline-block;
height: 100%; height: 100%;
vertical-align: middle; vertical-align: middle;
} }
#expression-wrapper { #expression-wrapper {
display: flex; display: flex;
height: calc(100vh - 40px); height: calc(100vh - 40px);
width: 100vw; width: 100vw;
} }
.expression-holder { .expression-holder {
min-width: 100px; min-width: 100px;
min-height: 100px; min-height: 100px;
max-height: 90vh; max-height: 90vh;
max-width: 90vh; max-width: 90vh;
width: calc((100vw - var(--sheldWidth)) /2); width: calc((100vw - var(--sheldWidth)) /2);
position: absolute; position: absolute;
bottom: 1px; bottom: 1px;
padding: 0; padding: 0;
filter: drop-shadow(2px 2px 2px #51515199); filter: drop-shadow(2px 2px 2px #51515199);
z-index: 2; z-index: 2;
overflow: hidden; overflow: hidden;
} }
img.expression { img.expression {
width: 100%; width: 100%;
height: 100%; height: 100%;
vertical-align: bottom; vertical-align: bottom;
object-fit: contain; object-fit: contain;
} }
img.expression[src=""] { img.expression[src=""] {
visibility: hidden; visibility: hidden;
} }
img.expression.default { img.expression.default {
vertical-align: middle; vertical-align: middle;
max-height: 120px; max-height: 120px;
object-fit: contain !important; object-fit: contain !important;
margin-top: 50px; margin-top: 50px;
} }
.debug-image { .debug-image {
display: none; display: none;
visibility: collapse; visibility: collapse;
opacity: 0; opacity: 0;
width: 0px; width: 0px;
height: 0px; height: 0px;
} }
.expression_list_item { .expression_list_item {
position: relative; position: relative;
max-width: 20%; max-width: 20%;
max-height: 200px; max-height: 200px;
background-color: #515151b0; background-color: #515151b0;
border-radius: 10px; border-radius: 10px;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.expression_list_title { .expression_list_title {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
text-align: center; text-align: center;
font-weight: 600; font-weight: 600;
background-color: #000000a8; background-color: #000000a8;
width: 100%; width: 100%;
height: 20%; height: 20%;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.expression_list_image { .expression_list_buttons {
max-width: 100%; position: absolute;
height: 100%; top: 0;
} left: 0;
width: 100%;
#image_list { display: flex;
display: flex; flex-direction: row;
flex-direction: row; justify-content: space-between;
column-gap: 1rem; align-items: center;
margin: 1rem; height: 20%;
flex-wrap: wrap; padding: 0.25rem;
justify-content: space-evenly; }
row-gap: 1rem;
} .expression_list_image {
max-width: 100%;
#image_list .success { height: 100%;
color: green; object-fit: cover;
} }
#image_list .failure { #image_list {
color: red; display: flex;
} flex-direction: row;
column-gap: 1rem;
.expression_settings p { margin: 1rem;
margin-top: 0.5rem; flex-wrap: wrap;
margin-bottom: 0.5rem; justify-content: space-evenly;
} row-gap: 1rem;
}
.expression_settings label {
display: flex; #image_list .success {
align-items: center; color: green;
flex-direction: row; }
margin-left: 0px;
} #image_list .failure {
color: red;
.expression_settings label input { }
margin-left: 0px !important;
} .expression_settings p {
margin-top: 0.5rem;
@media screen and (max-width:1200px) { margin-bottom: 0.5rem;
div.expression { }
display: none;
} .expression_settings label {
} display: flex;
align-items: center;
flex-direction: row;
margin-left: 0px;
}
.expression_settings label input {
margin-left: 0px !important;
}
@media screen and (max-width:1200px) {
div.expression {
display: none;
}
}

View File

@ -3061,6 +3061,83 @@ app.post('/google_translate', jsonParser, async (request, response) => {
} }
}); });
app.post('/delete_sprite', jsonParser, async (request, response) => {
const label = request.body.label;
const name = request.body.name;
if (!label || !name) {
return response.sendStatus(400);
}
try {
const spritesPath = path.join(directories.characters, name);
// No sprites folder exists, or not a directory
if (!fs.existsSync(spritesPath) || !fs.statSync(spritesPath).isDirectory()) {
return response.sendStatus(404);
}
const files = fs.readdirSync(spritesPath);
// Remove existing sprite with the same label
for (const file of files) {
if (path.parse(file).name === label) {
fs.rmSync(path.join(spritesPath, file));
}
}
return response.sendStatus(200);
} catch (error) {
console.error(error);
return response.sendStatus(500);
}
});
app.post('/upload_sprite', urlencodedParser, async (request, response) => {
const file = request.file;
const label = request.body.label;
const name = request.body.name;
if (!file || !label || !name) {
return response.sendStatus(400);
}
try {
const spritesPath = path.join(directories.characters, name);
// Path to sprites is not a directory. This should never happen.
if (!fs.statSync(spritesPath).isDirectory()) {
return response.sendStatus(404);
}
// Create sprites folder if it doesn't exist
if (!fs.existsSync(spritesPath)) {
fs.mkdirSync(spritesPath);
}
const files = fs.readdirSync(spritesPath);
// Remove existing sprite with the same label
for (const file of files) {
if (path.parse(file).name === label) {
fs.rmSync(path.join(spritesPath, file));
}
}
const filename = label + path.parse(file.originalname).ext;
const spritePath = path.join("./uploads/", file.filename);
const pathToFile = path.join(spritesPath, filename);
// Copy uploaded file to sprites folder
fs.cpSync(spritePath, pathToFile);
// Remove uploaded file
fs.rmSync(spritePath);
return response.sendStatus(200);
} catch (error) {
console.error(error);
return response.sendStatus(500);
}
});
function writeSecret(key, value) { function writeSecret(key, value) {
if (!fs.existsSync(SECRETS_FILE)) { if (!fs.existsSync(SECRETS_FILE)) {
const emptyFile = JSON.stringify({}); const emptyFile = JSON.stringify({});