Upload video bg via converter extension

d05373cdd2e8fbce1b3a71895ea3fa437e6047ac

Cohee <18619528+Cohee1207@users.noreply.github.com>

3 files changed, +54 -4Ignore whitespace
public/global.d.ts+11 -0
@@ -55,4 +55,15 @@ declare global {
55 * @param provider Translation provider55 * @param provider Translation provider
56 */56 */
57 async function translate(text: string, lang: string, provider: string = null): Promise<string>;57 async function translate(text: string, lang: string, provider: string = null): Promise<string>;
58
59 interface ConvertVideoArgs {
60 buffer: Uint8Array;
61 name: string;
62 }
63
64 /**
65 * Converts a video file to an animated WebP format using FFmpeg.
66 * @param args - The arguments for the conversion function.
67 */
68 function convertVideoToAnimatedWebp(args: ConvertVideoArgs): Promise<Uint8Array>;
58}69}
public/index.html+1 -1
@@ -4920,7 +4920,7 @@
4920 <div id="bg_menu_content" class="bg_list">4920 <div id="bg_menu_content" class="bg_list">
4921 <form id="form_bg_download" class="bg_example no-border no-shadow" action="javascript:void(null);" method="post" enctype="multipart/form-data">4921 <form id="form_bg_download" class="bg_example no-border no-shadow" action="javascript:void(null);" method="post" enctype="multipart/form-data">
4922 <label class="input-file">4922 <label class="input-file">
4923 <input type="file" id="add_bg_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif, image/bmp">4923 <input type="file" id="add_bg_button" name="avatar" accept="image/*, video/*">
4924 <div class="bg_example no-border no-shadow add_bg_but" style="background-image: url('/img/addbg3.png');"></div>4924 <div class="bg_example no-border no-shadow add_bg_but" style="background-image: url('/img/addbg3.png');"></div>
4925 </label>4925 </label>
4926 </form>4926 </form>
public/scripts/backgrounds.js+42 -3
@@ -1,7 +1,7 @@
1import { Fuse } from '../lib.js';1import { Fuse } from '../lib.js';
22
3import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveSettingsDebounced } from '../script.js';3import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveSettingsDebounced } from '../script.js';
4import { saveMetadataDebounced } from './extensions.js';4import { openThirdPartyExtensionMenu, saveMetadataDebounced } from './extensions.js';
5import { SlashCommand } from './slash-commands/SlashCommand.js';5import { SlashCommand } from './slash-commands/SlashCommand.js';
6import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';6import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
7import { flashHighlight, stringFormat } from './utils.js';7import { flashHighlight, stringFormat } from './utils.js';
@@ -77,7 +77,7 @@ function getChatBackgroundsList() {
77}77}
7878
79function getBackgroundPath(fileUrl) {79function getBackgroundPath(fileUrl) {
80 return `backgrounds/${fileUrl}`;80 return `backgrounds/${encodeURIComponent(fileUrl)}`;
81}81}
8282
83function highlightLockedBackground() {83function highlightLockedBackground() {
@@ -438,7 +438,7 @@ async function delBackground(bg) {
438 });438 });
439}439}
440440
441function onBackgroundUploadSelected() {441async function onBackgroundUploadSelected() {
442 const form = $('#form_bg_download').get(0);442 const form = $('#form_bg_download').get(0);
443443
444 if (!(form instanceof HTMLFormElement)) {444 if (!(form instanceof HTMLFormElement)) {
@@ -447,11 +447,50 @@ function onBackgroundUploadSelected() {
447 }447 }
448448
449 const formData = new FormData(form);449 const formData = new FormData(form);
450 await convertFileIfVideo(formData);
450 uploadBackground(formData);451 uploadBackground(formData);
451 form.reset();452 form.reset();
452}453}
453454
454/**455/**
456 * Converts a video file to an animated webp format if the file is a video.
457 * @param {FormData} formData
458 * @returns {Promise<void>}
459 */
460async function convertFileIfVideo(formData) {
461 const file = formData.get('avatar');
462 if (!(file instanceof File)) {
463 return;
464 }
465 if (!file.type.startsWith('video/')) {
466 return;
467 }
468 if (typeof globalThis.convertVideoToAnimatedWebp !== 'function') {
469 toastr.warning('Click here to install the Video Background Loader extension', 'Video background uploads require a downloadable add-on', {
470 timeOut: 0,
471 extendedTimeOut: 0,
472 onclick: () => openThirdPartyExtensionMenu('https://github.com/SillyTavern/Extension-VideoBackgroundLoader'),
473 });
474 return;
475 }
476
477 let toastMessage = jQuery();
478 try {
479 toastMessage = toastr.info('Preparing video for upload...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 });
480 const sourceBuffer = await file.arrayBuffer();
481 const convertedBuffer = await globalThis.convertVideoToAnimatedWebp({ buffer: new Uint8Array(sourceBuffer), name: file.name });
482 const convertedFileName = file.name.replace(/\.[^/.]+$/, '.webp');
483 const convertedFile = new File([convertedBuffer], convertedFileName, { type: 'image/webp' });
484 formData.set('avatar', convertedFile);
485 toastMessage.remove();
486 } catch (error) {
487 toastMessage.remove();
488 console.error('Error converting video to animated webp:', error);
489 toastr.error('Error converting video to animated webp');
490 }
491}
492
493/**
455 * Uploads a background to the server494 * Uploads a background to the server
456 * @param {FormData} formData495 * @param {FormData} formData
457 */496 */