mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Allow AVIF and friends for avatar images
This commit is contained in:
@ -4008,7 +4008,7 @@
|
|||||||
<div id="avatar_div" class="avatar_div alignitemsflexstart justifySpaceBetween flexnowrap flexGap5">
|
<div id="avatar_div" class="avatar_div alignitemsflexstart justifySpaceBetween flexnowrap flexGap5">
|
||||||
<label id="avatar_div_div" class="add_avatar avatar" for="add_avatar_button" title="Click to select a new avatar for this character" data-i18n="[title]Click to select a new avatar for this character">
|
<label id="avatar_div_div" class="add_avatar avatar" for="add_avatar_button" title="Click to select a new avatar for this character" data-i18n="[title]Click to select a new avatar for this character">
|
||||||
<img id="avatar_load_preview" src="img/ai4.png" alt="avatar">
|
<img id="avatar_load_preview" src="img/ai4.png" alt="avatar">
|
||||||
<input hidden type="file" id="add_avatar_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif, image/bmp">
|
<input hidden type="file" id="add_avatar_button" name="avatar" accept="image/*">
|
||||||
</label>
|
</label>
|
||||||
<div class="flex-container flexFlowColumn">
|
<div class="flex-container flexFlowColumn">
|
||||||
<div class="flex-container flexFlowColumn">
|
<div class="flex-container flexFlowColumn">
|
||||||
|
@ -150,6 +150,7 @@ import {
|
|||||||
humanFileSize,
|
humanFileSize,
|
||||||
Stopwatch,
|
Stopwatch,
|
||||||
isValidUrl,
|
isValidUrl,
|
||||||
|
ensureImageFormatSupported,
|
||||||
} from './scripts/utils.js';
|
} from './scripts/utils.js';
|
||||||
|
|
||||||
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js';
|
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js';
|
||||||
@ -5713,6 +5714,12 @@ async function uploadUserAvatar(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rawFile = formData.get('avatar');
|
||||||
|
if (rawFile instanceof File){
|
||||||
|
const convertedFile = await ensureImageFormatSupported(rawFile);
|
||||||
|
formData.set('avatar', convertedFile);
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: url,
|
url: url,
|
||||||
@ -7212,8 +7219,15 @@ function addAlternateGreeting(template, greeting, index, getArray) {
|
|||||||
|
|
||||||
async function createOrEditCharacter(e) {
|
async function createOrEditCharacter(e) {
|
||||||
$('#rm_info_avatar').html('');
|
$('#rm_info_avatar').html('');
|
||||||
var formData = new FormData($('#form_create').get(0));
|
const formData = new FormData($('#form_create').get(0));
|
||||||
formData.set('fav', fav_ch_checked);
|
formData.set('fav', fav_ch_checked);
|
||||||
|
|
||||||
|
const rawFile = formData.get('avatar');
|
||||||
|
if (rawFile instanceof File){
|
||||||
|
const convertedFile = await ensureImageFormatSupported(rawFile);
|
||||||
|
formData.set('avatar', convertedFile);
|
||||||
|
}
|
||||||
|
|
||||||
if ($('#form_create').attr('actiontype') == 'createcharacter') {
|
if ($('#form_create').attr('actiontype') == 'createcharacter') {
|
||||||
if ($('#character_name_pole').val().length > 0) {
|
if ($('#character_name_pole').val().length > 0) {
|
||||||
if (is_group_generating || is_send_press) {
|
if (is_group_generating || is_send_press) {
|
||||||
|
@ -1047,15 +1047,51 @@ export function loadFileToDocument(url, type) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that we can import war crime image formats like WEBP and AVIF.
|
||||||
|
* @param {File} file Input file
|
||||||
|
* @returns {Promise<File>} A promise that resolves to the supported file.
|
||||||
|
*/
|
||||||
|
export async function ensureImageFormatSupported(file) {
|
||||||
|
const supportedTypes = [
|
||||||
|
'image/jpeg',
|
||||||
|
'image/png',
|
||||||
|
'image/bmp',
|
||||||
|
'image/tiff',
|
||||||
|
'image/gif',
|
||||||
|
'image/apng',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (supportedTypes.includes(file.type) || !file.type.startsWith('image/')) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await convertImageFile(file, 'image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an image file to a given format.
|
||||||
|
* @param {File} inputFile File to convert
|
||||||
|
* @param {string} type Target file type
|
||||||
|
* @returns {Promise<File>} A promise that resolves to the converted file.
|
||||||
|
*/
|
||||||
|
export async function convertImageFile(inputFile, type = 'image/png') {
|
||||||
|
const base64 = await getBase64Async(inputFile);
|
||||||
|
const thumbnail = await createThumbnail(base64, null, null, type);
|
||||||
|
const blob = await fetch(thumbnail).then(res => res.blob());
|
||||||
|
const outputFile = new File([blob], inputFile.name, { type });
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a thumbnail from a data URL.
|
* Creates a thumbnail from a data URL.
|
||||||
* @param {string} dataUrl The data URL encoded data of the image.
|
* @param {string} dataUrl The data URL encoded data of the image.
|
||||||
* @param {number} maxWidth The maximum width of the thumbnail.
|
* @param {number|null} maxWidth The maximum width of the thumbnail.
|
||||||
* @param {number} maxHeight The maximum height of the thumbnail.
|
* @param {number|null} maxHeight The maximum height of the thumbnail.
|
||||||
* @param {string} [type='image/jpeg'] The type of the thumbnail.
|
* @param {string} [type='image/jpeg'] The type of the thumbnail.
|
||||||
* @returns {Promise<string>} A promise that resolves to the thumbnail data URL.
|
* @returns {Promise<string>} A promise that resolves to the thumbnail data URL.
|
||||||
*/
|
*/
|
||||||
export function createThumbnail(dataUrl, maxWidth, maxHeight, type = 'image/jpeg') {
|
export function createThumbnail(dataUrl, maxWidth = null, maxHeight = null, type = 'image/jpeg') {
|
||||||
// Someone might pass in a base64 encoded string without the data URL prefix
|
// Someone might pass in a base64 encoded string without the data URL prefix
|
||||||
if (!dataUrl.includes('data:')) {
|
if (!dataUrl.includes('data:')) {
|
||||||
dataUrl = `data:image/jpeg;base64,${dataUrl}`;
|
dataUrl = `data:image/jpeg;base64,${dataUrl}`;
|
||||||
@ -1073,6 +1109,16 @@ export function createThumbnail(dataUrl, maxWidth, maxHeight, type = 'image/jpeg
|
|||||||
let thumbnailWidth = maxWidth;
|
let thumbnailWidth = maxWidth;
|
||||||
let thumbnailHeight = maxHeight;
|
let thumbnailHeight = maxHeight;
|
||||||
|
|
||||||
|
if (maxWidth === null) {
|
||||||
|
thumbnailWidth = img.width;
|
||||||
|
maxWidth = img.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxHeight === null) {
|
||||||
|
thumbnailHeight = img.height;
|
||||||
|
maxHeight = img.height;
|
||||||
|
}
|
||||||
|
|
||||||
if (img.width > img.height) {
|
if (img.width > img.height) {
|
||||||
thumbnailHeight = maxWidth / aspectRatio;
|
thumbnailHeight = maxWidth / aspectRatio;
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user