mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-01-31 03:27:44 +01:00
Exorcised base64 image stuff
This commit is contained in:
parent
4488cee65d
commit
2f09efcd7f
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@ public/backgrounds/
|
||||
public/groups/
|
||||
public/group chats/
|
||||
public/worlds/
|
||||
public/user/
|
||||
public/css/bg_load.css
|
||||
public/themes/
|
||||
public/OpenAI Settings/
|
||||
|
@ -5629,7 +5629,14 @@ export async function getChatsFromFiles(data, isGroupChat) {
|
||||
return chat_dict;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the metadata of all past chats related to a specific character based on its avatar URL.
|
||||
* The function sends a POST request to the server to retrieve all chats for the character. It then
|
||||
* processes the received data, sorts it by the file name, and returns the sorted data.
|
||||
*
|
||||
* @returns {Array} - An array containing metadata of all past chats of the character, sorted
|
||||
* in descending order by file name. Returns `undefined` if the fetch request is unsuccessful.
|
||||
*/
|
||||
async function getPastCharacterChats() {
|
||||
const response = await fetch("/getallchatsofcharacter", {
|
||||
method: 'POST',
|
||||
|
@ -537,6 +537,38 @@ function processReply(str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
async function saveBase64AsFile(base64Data, characterName, ext) {
|
||||
// Construct the full data URL
|
||||
const format = ext; // Extract the file extension (jpg, png, webp)
|
||||
const dataURL = `data:image/${format};base64,${base64Data}`;
|
||||
|
||||
// Prepare the request body
|
||||
const requestBody = {
|
||||
image: dataURL,
|
||||
ch_name: characterName
|
||||
};
|
||||
|
||||
// Send the data URL to your backend using fetch
|
||||
const response = await fetch('/uploadimage', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(requestBody),
|
||||
headers: {
|
||||
...getRequestHeaders(),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
});
|
||||
|
||||
// If the response is successful, get the saved image path from the server's response
|
||||
if (response.ok) {
|
||||
const responseData = await response.json();
|
||||
return responseData.path;
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to upload the image to the server');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getRawLastMessage() {
|
||||
const context = getContext();
|
||||
const lastMessage = context.chat.slice(-1)[0].mes,
|
||||
@ -565,6 +597,10 @@ async function generatePicture(_, trigger, message, callback) {
|
||||
const quiet_prompt = getQuietPrompt(generationType, trigger);
|
||||
const context = getContext();
|
||||
|
||||
// if context.characterId is not null, then we get context.characters[context.characterId].avatar, else we get groupId and context.groups[groupId].id
|
||||
// sadly, groups is not an array, but is a dict with keys being index numbers, so we have to filter it
|
||||
const characterName = context.characterId ? context.characters[context.characterId].name : context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]].id.toString();
|
||||
|
||||
const prevSDHeight = extension_settings.sd.height;
|
||||
const prevSDWidth = extension_settings.sd.width;
|
||||
const aspectRatio = extension_settings.sd.width / extension_settings.sd.height;
|
||||
@ -580,8 +616,10 @@ async function generatePicture(_, trigger, message, callback) {
|
||||
// Round to nearest multiple of 64
|
||||
extension_settings.sd.width = Math.round(extension_settings.sd.height * 1.8 / 64) * 64;
|
||||
const callbackOriginal = callback;
|
||||
callback = function (prompt, base64Image) {
|
||||
callback = async function (prompt, base64Image) {
|
||||
const imagePath = base64Image;
|
||||
const imgUrl = `url(${base64Image})`;
|
||||
|
||||
if ('forceSetBackground' in window) {
|
||||
forceSetBackground(imgUrl);
|
||||
} else {
|
||||
@ -590,9 +628,9 @@ async function generatePicture(_, trigger, message, callback) {
|
||||
}
|
||||
|
||||
if (typeof callbackOriginal === 'function') {
|
||||
callbackOriginal(prompt, base64Image);
|
||||
callbackOriginal(prompt, imagePath);
|
||||
} else {
|
||||
sendMessage(prompt, base64Image);
|
||||
sendMessage(prompt, imagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -604,7 +642,7 @@ async function generatePicture(_, trigger, message, callback) {
|
||||
context.deactivateSendButtons();
|
||||
hideSwipeButtons();
|
||||
|
||||
await sendGenerationRequest(generationType, prompt, callback);
|
||||
await sendGenerationRequest(generationType, prompt, characterName, callback);
|
||||
} catch (err) {
|
||||
console.trace(err);
|
||||
throw new Error('SD prompt text generation failed.')
|
||||
@ -644,19 +682,19 @@ async function generatePrompt(quiet_prompt) {
|
||||
return processReply(reply);
|
||||
}
|
||||
|
||||
async function sendGenerationRequest(generationType, prompt, callback) {
|
||||
async function sendGenerationRequest(generationType, prompt, characterName=null, callback) {
|
||||
const prefix = generationType !== generationMode.BACKGROUND
|
||||
? combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix())
|
||||
: extension_settings.sd.prompt_prefix;
|
||||
|
||||
if (extension_settings.sd.horde) {
|
||||
await generateHordeImage(prompt, prefix, callback);
|
||||
await generateHordeImage(prompt, prefix, characterName, callback);
|
||||
} else {
|
||||
await generateExtrasImage(prompt, prefix, callback);
|
||||
await generateExtrasImage(prompt, prefix, characterName, callback);
|
||||
}
|
||||
}
|
||||
|
||||
async function generateExtrasImage(prompt, prefix, callback) {
|
||||
async function generateExtrasImage(prompt, prefix, characterName, callback) {
|
||||
console.debug(extension_settings.sd);
|
||||
const url = new URL(getApiUrl());
|
||||
url.pathname = '/api/image';
|
||||
@ -680,14 +718,14 @@ async function generateExtrasImage(prompt, prefix, callback) {
|
||||
|
||||
if (result.ok) {
|
||||
const data = await result.json();
|
||||
const base64Image = `data:image/jpeg;base64,${data.image}`;
|
||||
const base64Image = await saveBase64AsFile(data.image, characterName, "jpg");
|
||||
callback ? callback(prompt, base64Image) : sendMessage(prompt, base64Image);
|
||||
} else {
|
||||
callPopup('Image generation has failed. Please try again.', 'text');
|
||||
}
|
||||
}
|
||||
|
||||
async function generateHordeImage(prompt, prefix, callback) {
|
||||
async function generateHordeImage(prompt, prefix, characterName, callback) {
|
||||
const result = await fetch('/horde_generateimage', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
@ -709,7 +747,8 @@ async function generateHordeImage(prompt, prefix, callback) {
|
||||
|
||||
if (result.ok) {
|
||||
const data = await result.text();
|
||||
const base64Image = `data:image/webp;base64,${data}`;
|
||||
|
||||
const base64Image = await saveBase64AsFile(data, characterName, "webp");
|
||||
callback ? callback(prompt, base64Image) : sendMessage(prompt, base64Image);
|
||||
} else {
|
||||
toastr.error('Image generation has failed. Please try again.');
|
||||
@ -842,7 +881,7 @@ async function sdMessageButton(e) {
|
||||
message.extra.title = prompt;
|
||||
|
||||
console.log('Regenerating an image, using existing prompt:', prompt);
|
||||
await sendGenerationRequest(generationMode.FREE, prompt, saveGeneratedImage);
|
||||
await sendGenerationRequest(generationMode.FREE, prompt, characterName, saveGeneratedImage);
|
||||
}
|
||||
else {
|
||||
console.log("doing /sd raw last");
|
||||
|
49
server.js
49
server.js
@ -296,6 +296,8 @@ const baseRequestArgs = { headers: { "Content-Type": "application/json" } };
|
||||
const directories = {
|
||||
worlds: 'public/worlds/',
|
||||
avatars: 'public/User Avatars',
|
||||
images: 'public/img/',
|
||||
userImages: 'public/user/images/',
|
||||
groups: 'public/groups/',
|
||||
groupChats: 'public/group chats',
|
||||
chats: 'public/chats/',
|
||||
@ -2585,6 +2587,53 @@ app.post('/uploaduseravatar', urlencodedParser, async (request, response) => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
function ensureDirectoryExistence(filePath) {
|
||||
const dirname = path.dirname(filePath);
|
||||
if (fs.existsSync(dirname)) {
|
||||
return true;
|
||||
}
|
||||
ensureDirectoryExistence(dirname);
|
||||
fs.mkdirSync(dirname);
|
||||
}
|
||||
|
||||
app.post('/uploadimage', jsonParser, async (request, response) => {
|
||||
// Check for image data
|
||||
if (!request.body || !request.body.image) {
|
||||
return response.status(400).send({ error: "No image data provided" });
|
||||
}
|
||||
|
||||
// Extracting the base64 data and the image format
|
||||
const match = request.body.image.match(/^data:image\/(png|jpg|webp);base64,(.+)$/);
|
||||
if (!match) {
|
||||
return response.status(400).send({ error: "Invalid image format" });
|
||||
}
|
||||
|
||||
const [, format, base64Data] = match;
|
||||
|
||||
// Constructing filename and path
|
||||
const filename = `${Date.now()}.${format}`;
|
||||
// if character is defined, save to a sub folder for that character
|
||||
let pathToNewFile = path.join(directories.userImages, filename);
|
||||
if(request.body.ch_name){
|
||||
pathToNewFile = path.join(directories.userImages, request.body.ch_name, filename);
|
||||
}
|
||||
|
||||
try {
|
||||
ensureDirectoryExistence(pathToNewFile);
|
||||
const imageBuffer = Buffer.from(base64Data, 'base64');
|
||||
await fs.promises.writeFile(pathToNewFile, imageBuffer);
|
||||
// send the path to the image, relative to the client folder, which means removing the first folder from the path which is 'public'
|
||||
pathToNewFile = pathToNewFile.split(path.sep).slice(1).join(path.sep);
|
||||
response.send({ path:pathToNewFile });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
response.status(500).send({ error: "Failed to save the image" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.post('/getgroups', jsonParser, (_, response) => {
|
||||
const groups = [];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user