mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🖼️ feat: Avatar GIF Support & Dynamic Extensions (#7657)
This commit is contained in:
parent
aca89091d9
commit
442b149d55
7 changed files with 58 additions and 10 deletions
|
|
@ -18,6 +18,7 @@ const {
|
|||
} = require('~/models/Agent');
|
||||
const { uploadImageBuffer, filterFile } = require('~/server/services/Files/process');
|
||||
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
||||
const { resizeAvatar } = require('~/server/services/Files/images/avatar');
|
||||
const { refreshS3Url } = require('~/server/services/Files/S3/crud');
|
||||
const { updateAction, getActions } = require('~/models/Action');
|
||||
const { updateAgentProjects } = require('~/models/Agent');
|
||||
|
|
@ -373,12 +374,26 @@ const uploadAgentAvatarHandler = async (req, res) => {
|
|||
}
|
||||
|
||||
const buffer = await fs.readFile(req.file.path);
|
||||
const image = await uploadImageBuffer({
|
||||
req,
|
||||
context: FileContext.avatar,
|
||||
metadata: { buffer },
|
||||
|
||||
const fileStrategy = req.app.locals.fileStrategy;
|
||||
|
||||
const resizedBuffer = await resizeAvatar({
|
||||
userId: req.user.id,
|
||||
input: buffer,
|
||||
});
|
||||
|
||||
const { processAvatar } = getStrategyFunctions(fileStrategy);
|
||||
const avatarUrl = await processAvatar({
|
||||
buffer: resizedBuffer,
|
||||
userId: req.user.id,
|
||||
manual: 'false',
|
||||
});
|
||||
|
||||
const image = {
|
||||
filepath: avatarUrl,
|
||||
source: fileStrategy,
|
||||
};
|
||||
|
||||
let _avatar;
|
||||
try {
|
||||
const agent = await getAgent({ id: agent_id });
|
||||
|
|
@ -403,7 +418,7 @@ const uploadAgentAvatarHandler = async (req, res) => {
|
|||
const data = {
|
||||
avatar: {
|
||||
filepath: image.filepath,
|
||||
source: req.app.locals.fileStrategy,
|
||||
source: image.source,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -97,10 +97,14 @@ async function prepareAzureImageURL(req, file) {
|
|||
*/
|
||||
async function processAzureAvatar({ buffer, userId, manual, basePath = 'images', containerName }) {
|
||||
try {
|
||||
const metadata = await sharp(buffer).metadata();
|
||||
const extension = metadata.format === 'gif' ? 'gif' : 'png';
|
||||
const fileName = `avatar.${extension}`;
|
||||
|
||||
const downloadURL = await saveBufferToAzure({
|
||||
userId,
|
||||
buffer,
|
||||
fileName: 'avatar.png',
|
||||
fileName,
|
||||
basePath,
|
||||
containerName,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -87,10 +87,14 @@ async function prepareImageURL(req, file) {
|
|||
*/
|
||||
async function processFirebaseAvatar({ buffer, userId, manual }) {
|
||||
try {
|
||||
const metadata = await sharp(buffer).metadata();
|
||||
const extension = metadata.format === 'gif' ? 'gif' : 'png';
|
||||
const fileName = `avatar.${extension}`;
|
||||
|
||||
const downloadURL = await saveBufferToFirebase({
|
||||
userId,
|
||||
buffer,
|
||||
fileName: 'avatar.png',
|
||||
fileName,
|
||||
});
|
||||
|
||||
const isManual = manual === 'true';
|
||||
|
|
|
|||
|
|
@ -129,7 +129,10 @@ async function processLocalAvatar({ buffer, userId, manual }) {
|
|||
userId,
|
||||
);
|
||||
|
||||
const fileName = `avatar-${new Date().getTime()}.png`;
|
||||
const metadata = await sharp(buffer).metadata();
|
||||
const extension = metadata.format === 'gif' ? 'gif' : 'png';
|
||||
|
||||
const fileName = `avatar-${new Date().getTime()}.${extension}`;
|
||||
const urlRoute = `/images/${userId}/${fileName}`;
|
||||
const avatarPath = path.join(userDir, fileName);
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,11 @@ async function prepareImageURLS3(req, file) {
|
|||
*/
|
||||
async function processS3Avatar({ buffer, userId, manual, basePath = defaultBasePath }) {
|
||||
try {
|
||||
const downloadURL = await saveBufferToS3({ userId, buffer, fileName: 'avatar.png', basePath });
|
||||
const metadata = await sharp(buffer).metadata();
|
||||
const extension = metadata.format === 'gif' ? 'gif' : 'png';
|
||||
const fileName = `avatar.${extension}`;
|
||||
|
||||
const downloadURL = await saveBufferToS3({ userId, buffer, fileName, basePath });
|
||||
if (manual === 'true') {
|
||||
await updateUser(userId, { avatar: downloadURL });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,25 @@ async function resizeAvatar({ userId, input, desiredFormat = EImageOutputType.PN
|
|||
throw new Error('Invalid input type. Expected URL, Buffer, or File.');
|
||||
}
|
||||
|
||||
const { width, height } = await sharp(imageBuffer).metadata();
|
||||
const metadata = await sharp(imageBuffer).metadata();
|
||||
const { width, height } = metadata;
|
||||
const minSize = Math.min(width, height);
|
||||
|
||||
if (metadata.format === 'gif') {
|
||||
const resizedBuffer = await sharp(imageBuffer, { animated: true })
|
||||
.extract({
|
||||
left: Math.floor((width - minSize) / 2),
|
||||
top: Math.floor((height - minSize) / 2),
|
||||
width: minSize,
|
||||
height: minSize,
|
||||
})
|
||||
.resize(250, 250)
|
||||
.gif()
|
||||
.toBuffer();
|
||||
|
||||
return resizedBuffer;
|
||||
}
|
||||
|
||||
const squaredBuffer = await sharp(imageBuffer)
|
||||
.extract({
|
||||
left: Math.floor((width - minSize) / 2),
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ export const AgentAvatarRender = ({
|
|||
width="80"
|
||||
height="80"
|
||||
style={{ opacity: progress < 1 ? 0.4 : 1 }}
|
||||
key={url || 'default-key'}
|
||||
/>
|
||||
{progress < 1 && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/5 text-white">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue