From d05373cdd2e8fbce1b3a71895ea3fa437e6047ac Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 3 Apr 2025 23:48:01 +0300 Subject: [PATCH 1/4] Upload video bg via converter extension --- public/global.d.ts | 11 +++++++++ public/index.html | 2 +- public/scripts/backgrounds.js | 45 ++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/public/global.d.ts b/public/global.d.ts index 670b291a9..c860f9028 100644 --- a/public/global.d.ts +++ b/public/global.d.ts @@ -55,4 +55,15 @@ declare global { * @param provider Translation provider */ async function translate(text: string, lang: string, provider: string = null): Promise; + + interface ConvertVideoArgs { + buffer: Uint8Array; + name: string; + } + + /** + * Converts a video file to an animated WebP format using FFmpeg. + * @param args - The arguments for the conversion function. + */ + function convertVideoToAnimatedWebp(args: ConvertVideoArgs): Promise; } diff --git a/public/index.html b/public/index.html index 3750c193e..c014eea2a 100644 --- a/public/index.html +++ b/public/index.html @@ -4920,7 +4920,7 @@
diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js index 308aac532..6e07d012c 100644 --- a/public/scripts/backgrounds.js +++ b/public/scripts/backgrounds.js @@ -1,7 +1,7 @@ import { Fuse } from '../lib.js'; import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveSettingsDebounced } from '../script.js'; -import { saveMetadataDebounced } from './extensions.js'; +import { openThirdPartyExtensionMenu, saveMetadataDebounced } from './extensions.js'; import { SlashCommand } from './slash-commands/SlashCommand.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { flashHighlight, stringFormat } from './utils.js'; @@ -77,7 +77,7 @@ function getChatBackgroundsList() { } function getBackgroundPath(fileUrl) { - return `backgrounds/${fileUrl}`; + return `backgrounds/${encodeURIComponent(fileUrl)}`; } function highlightLockedBackground() { @@ -438,7 +438,7 @@ async function delBackground(bg) { }); } -function onBackgroundUploadSelected() { +async function onBackgroundUploadSelected() { const form = $('#form_bg_download').get(0); if (!(form instanceof HTMLFormElement)) { @@ -447,10 +447,49 @@ function onBackgroundUploadSelected() { } const formData = new FormData(form); + await convertFileIfVideo(formData); uploadBackground(formData); form.reset(); } +/** + * Converts a video file to an animated webp format if the file is a video. + * @param {FormData} formData + * @returns {Promise} + */ +async function convertFileIfVideo(formData) { + const file = formData.get('avatar'); + if (!(file instanceof File)) { + return; + } + if (!file.type.startsWith('video/')) { + return; + } + if (typeof globalThis.convertVideoToAnimatedWebp !== 'function') { + toastr.warning('Click here to install the Video Background Loader extension', 'Video background uploads require a downloadable add-on', { + timeOut: 0, + extendedTimeOut: 0, + onclick: () => openThirdPartyExtensionMenu('https://github.com/SillyTavern/Extension-VideoBackgroundLoader'), + }); + return; + } + + let toastMessage = jQuery(); + try { + toastMessage = toastr.info('Preparing video for upload...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 }); + const sourceBuffer = await file.arrayBuffer(); + const convertedBuffer = await globalThis.convertVideoToAnimatedWebp({ buffer: new Uint8Array(sourceBuffer), name: file.name }); + const convertedFileName = file.name.replace(/\.[^/.]+$/, '.webp'); + const convertedFile = new File([convertedBuffer], convertedFileName, { type: 'image/webp' }); + formData.set('avatar', convertedFile); + toastMessage.remove(); + } catch (error) { + toastMessage.remove(); + console.error('Error converting video to animated webp:', error); + toastr.error('Error converting video to animated webp'); + } +} + /** * Uploads a background to the server * @param {FormData} formData From e9178e52eb5b138235aaf9ee7ada9e8017196402 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 9 Apr 2025 19:45:33 +0300 Subject: [PATCH 2/4] Update upload to use fetch --- public/scripts/backgrounds.js | 53 ++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js index 6e07d012c..0e7af1feb 100644 --- a/public/scripts/backgrounds.js +++ b/public/scripts/backgrounds.js @@ -217,7 +217,7 @@ async function onCopyToSystemBackgroundClick(e) { const formData = new FormData(); formData.set('avatar', file); - uploadBackground(formData); + await uploadBackground(formData); const list = chat_metadata[LIST_METADATA_KEY] || []; const index = list.indexOf(bgNames.oldBg); @@ -448,7 +448,7 @@ async function onBackgroundUploadSelected() { const formData = new FormData(form); await convertFileIfVideo(formData); - uploadBackground(formData); + await uploadBackground(formData); form.reset(); } @@ -484,6 +484,7 @@ async function convertFileIfVideo(formData) { formData.set('avatar', convertedFile); toastMessage.remove(); } catch (error) { + formData.delete('avatar'); toastMessage.remove(); console.error('Error converting video to animated webp:', error); toastr.error('Error converting video to animated webp'); @@ -494,26 +495,34 @@ async function convertFileIfVideo(formData) { * Uploads a background to the server * @param {FormData} formData */ -function uploadBackground(formData) { - jQuery.ajax({ - type: 'POST', - url: '/api/backgrounds/upload', - data: formData, - beforeSend: function () { - }, - cache: false, - contentType: false, - processData: false, - success: async function (bg) { - setBackground(bg, generateUrlParameter(bg, false)); - await getBackgrounds(); - highlightNewBackground(bg); - }, - error: function (jqXHR, exception) { - console.log(exception); - console.log(jqXHR); - }, - }); +async function uploadBackground(formData) { + try { + if (!formData.has('avatar')) { + console.log('No file provided. Background upload cancelled.'); + return; + } + + const headers = getRequestHeaders(); + delete headers['Content-Type']; + + const response = await fetch('/api/backgrounds/upload', { + method: 'POST', + headers: getRequestHeaders(), + body: formData, + cache: 'no-cache', + }); + + if (!response.ok) { + throw new Error('Failed to upload background'); + } + + const bg = await response.text(); + setBackground(bg, generateUrlParameter(bg, false)); + await getBackgrounds(); + highlightNewBackground(bg); + } catch (error) { + console.error('Error uploading background:', error); + } } /** From 471004b828203cc843136d57aab7278579078265 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 9 Apr 2025 19:57:07 +0300 Subject: [PATCH 3/4] Skill issue --- public/scripts/backgrounds.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js index 0e7af1feb..a402939ba 100644 --- a/public/scripts/backgrounds.js +++ b/public/scripts/backgrounds.js @@ -507,7 +507,7 @@ async function uploadBackground(formData) { const response = await fetch('/api/backgrounds/upload', { method: 'POST', - headers: getRequestHeaders(), + headers: headers, body: formData, cache: 'no-cache', }); From 491752599c694155c44e9d2fbd26d6b3fa10e96b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:36:00 +0300 Subject: [PATCH 4/4] Localize messages --- public/scripts/backgrounds.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js index a402939ba..895905a06 100644 --- a/public/scripts/backgrounds.js +++ b/public/scripts/backgrounds.js @@ -466,7 +466,7 @@ async function convertFileIfVideo(formData) { return; } if (typeof globalThis.convertVideoToAnimatedWebp !== 'function') { - toastr.warning('Click here to install the Video Background Loader extension', 'Video background uploads require a downloadable add-on', { + toastr.warning(t`Click here to install the Video Background Loader extension`, t`Video background uploads require a downloadable add-on`, { timeOut: 0, extendedTimeOut: 0, onclick: () => openThirdPartyExtensionMenu('https://github.com/SillyTavern/Extension-VideoBackgroundLoader'), @@ -476,7 +476,7 @@ async function convertFileIfVideo(formData) { let toastMessage = jQuery(); try { - toastMessage = toastr.info('Preparing video for upload...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 }); + toastMessage = toastr.info(t`Preparing video for upload. This may take several minutes.`, t`Please wait`, { timeOut: 0, extendedTimeOut: 0 }); const sourceBuffer = await file.arrayBuffer(); const convertedBuffer = await globalThis.convertVideoToAnimatedWebp({ buffer: new Uint8Array(sourceBuffer), name: file.name }); const convertedFileName = file.name.replace(/\.[^/.]+$/, '.webp'); @@ -487,7 +487,7 @@ async function convertFileIfVideo(formData) { formData.delete('avatar'); toastMessage.remove(); console.error('Error converting video to animated webp:', error); - toastr.error('Error converting video to animated webp'); + toastr.error(t`Error converting video to animated webp`); } }