Merge branch 'staging' into pin-styles
| @@ -55,4 +55,15 @@ declare global { | ||
| 55 | 55 | * @param provider Translation provider |
| 56 | 56 | */ |
| 57 | 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 | } |
| @@ -4968,7 +4968,7 @@ | ||
| 4968 | 4968 | <div id="bg_menu_content" class="bg_list"> |
| 4969 | 4969 | <form id="form_bg_download" class="bg_example no-border no-shadow" action="javascript:void(null);" method="post" enctype="multipart/form-data"> |
| 4970 | 4970 | <label class="input-file"> |
| 4971 | 4971 | <input type="file" id="add_bg_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif*, imagevideo/bmp*"> |
| 4972 | 4972 | <div class="bg_example no-border no-shadow add_bg_but" style="background-image: url('/img/addbg3.png');"></div> |
| 4973 | 4973 | </label> |
| 4974 | 4974 | </form> |
| @@ -9154,7 +9154,7 @@ function formatSwipeCounter(current, total) { | ||
| 9154 | 9154 | * @param {string} [params.source] The source of the swipe event. |
| 9155 | 9155 | * @param {boolean} [params.repeated] Is the swipe event repeated. |
| 9156 | 9156 | */ |
| 9157 | 9157 | export function swipe_left(_event, { source, repeated } = {}) { |
| 9158 | 9158 | if (chat.length - 1 === Number(this_edit_mes_id)) { |
| 9159 | 9159 | closeMessageEditor(); |
| 9160 | 9160 | } |
| @@ -9302,7 +9302,7 @@ function swipe_left(_event, { source, repeated } = {}) { | ||
| 9302 | 9302 | * @param {string} [params.source] The source of the swipe event. |
| 9303 | 9303 | * @param {boolean} [params.repeated] Is the swipe event repeated. |
| 9304 | 9304 | */ |
| 9305 | 9305 | export function swipe_right(_event, { source, repeated } = {}) { |
| 9306 | 9306 | if (chat.length - 1 === Number(this_edit_mes_id)) { |
| 9307 | 9307 | closeMessageEditor(); |
| 9308 | 9308 | } |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { Fuse } from '../lib.js'; |
| 2 | 2 | |
| 3 | 3 | import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveSettingsDebounced } from '../script.js'; |
| 4 | 4 | import { openThirdPartyExtensionMenu, saveMetadataDebounced } from './extensions.js'; |
| 5 | 5 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 6 | 6 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 7 | 7 | import { flashHighlight, stringFormat } from './utils.js'; |
| @@ -78,7 +78,7 @@ function getChatBackgroundsList() { | ||
| 78 | 78 | } |
| 79 | 79 | |
| 80 | 80 | function getBackgroundPath(fileUrl) { |
| 81 | 81 | return `backgrounds/${encodeURIComponent(fileUrl)}`; |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | function highlightLockedBackground() { |
| @@ -218,7 +218,7 @@ async function onCopyToSystemBackgroundClick(e) { | ||
| 218 | 218 | const formData = new FormData(); |
| 219 | 219 | formData.set('avatar', file); |
| 220 | 220 | |
| 221 | 221 | await uploadBackground(formData); |
| 222 | 222 | |
| 223 | 223 | const list = chat_metadata[LIST_METADATA_KEY] || []; |
| 224 | 224 | const index = list.indexOf(bgNames.oldBg); |
| @@ -439,7 +439,7 @@ async function delBackground(bg) { | ||
| 439 | 439 | }); |
| 440 | 440 | } |
| 441 | 441 | |
| 442 | 442 | async function onBackgroundUploadSelected() { |
| 443 | 443 | const form = $('#form_bg_download').get(0); |
| 444 | 444 | |
| 445 | 445 | if (!(form instanceof HTMLFormElement)) { |
| @@ -448,34 +448,82 @@ function onBackgroundUploadSelected() { | ||
| 448 | 448 | } |
| 449 | 449 | |
| 450 | 450 | const formData = new FormData(form); |
| 451 | 451 | uploadBackgroundawait convertFileIfVideo(formData); |
| 452 | + await uploadBackground(formData); | |
| 452 | 453 | form.reset(); |
| 453 | 454 | } |
| 454 | 455 | |
| 455 | 456 | /** |
| 457 | + * Converts a video file to an animated webp format if the file is a video. | |
| 458 | + * @param {FormData} formData | |
| 459 | + * @returns {Promise<void>} | |
| 460 | + */ | |
| 461 | +async function convertFileIfVideo(formData) { | |
| 462 | + const file = formData.get('avatar'); | |
| 463 | + if (!(file instanceof File)) { | |
| 464 | + return; | |
| 465 | + } | |
| 466 | + if (!file.type.startsWith('video/')) { | |
| 467 | + return; | |
| 468 | + } | |
| 469 | + if (typeof globalThis.convertVideoToAnimatedWebp !== 'function') { | |
| 470 | + toastr.warning(t`Click here to install the Video Background Loader extension`, t`Video background uploads require a downloadable add-on`, { | |
| 471 | + timeOut: 0, | |
| 472 | + extendedTimeOut: 0, | |
| 473 | + onclick: () => openThirdPartyExtensionMenu('https://github.com/SillyTavern/Extension-VideoBackgroundLoader'), | |
| 474 | + }); | |
| 475 | + return; | |
| 476 | + } | |
| 477 | + | |
| 478 | + let toastMessage = jQuery(); | |
| 479 | + try { | |
| 480 | + toastMessage = toastr.info(t`Preparing video for upload. This may take several minutes.`, t`Please wait`, { timeOut: 0, extendedTimeOut: 0 }); | |
| 481 | + const sourceBuffer = await file.arrayBuffer(); | |
| 482 | + const convertedBuffer = await globalThis.convertVideoToAnimatedWebp({ buffer: new Uint8Array(sourceBuffer), name: file.name }); | |
| 483 | + const convertedFileName = file.name.replace(/\.[^/.]+$/, '.webp'); | |
| 484 | + const convertedFile = new File([convertedBuffer], convertedFileName, { type: 'image/webp' }); | |
| 485 | + formData.set('avatar', convertedFile); | |
| 486 | + toastMessage.remove(); | |
| 487 | + } catch (error) { | |
| 488 | + formData.delete('avatar'); | |
| 489 | + toastMessage.remove(); | |
| 490 | + console.error('Error converting video to animated webp:', error); | |
| 491 | + toastr.error(t`Error converting video to animated webp`); | |
| 492 | + } | |
| 493 | +} | |
| 494 | + | |
| 495 | +/** | |
| 456 | 496 | * Uploads a background to the server |
| 457 | 497 | * @param {FormData} formData |
| 458 | 498 | */ |
| 459 | 499 | async function uploadBackground(formData) { |
| 460 | - jQuery.ajax({ | |
| 500 | + try { | |
| 461 | - type: 'POST', | |
| 501 | + if (!formData.has('avatar')) { | |
| 462 | - url: '/api/backgrounds/upload', | |
| 502 | + console.log('No file provided. Background upload cancelled.'); | |
| 463 | - data: formData, | |
| 503 | + return; | |
| 464 | - beforeSend: function () { | |
| 504 | + } | |
| 465 | - }, | |
| 505 | + | |
| 466 | - cache: false, | |
| 506 | + const headers = getRequestHeaders(); | |
| 467 | - contentType: false, | |
| 507 | + delete headers['Content-Type']; | |
| 468 | - processData: false, | |
| 508 | + | |
| 469 | - success: async function (bg) { | |
| 509 | + const response = await fetch('/api/backgrounds/upload', { | |
| 470 | - setBackground(bg, generateUrlParameter(bg, false)); | |
| 510 | + method: 'POST', | |
| 471 | - await getBackgrounds(); | |
| 511 | + headers: headers, | |
| 472 | - highlightNewBackground(bg); | |
| 512 | + body: formData, | |
| 473 | - }, | |
| 513 | + cache: 'no-cache', | |
| 474 | - error: function (jqXHR, exception) { | |
| 514 | + }); | |
| 475 | - console.log(exception); | |
| 515 | + | |
| 476 | - console.log(jqXHR); | |
| 516 | + if (!response.ok) { | |
| 477 | - }, | |
| 517 | + throw new Error('Failed to upload background'); | |
| 478 | - }); | |
| 518 | + } | |
| 519 | + | |
| 520 | + const bg = await response.text(); | |
| 521 | + setBackground(bg, generateUrlParameter(bg, false)); | |
| 522 | + await getBackgrounds(); | |
| 523 | + highlightNewBackground(bg); | |
| 524 | + } catch (error) { | |
| 525 | + console.error('Error uploading background:', error); | |
| 526 | + } | |
| 479 | 527 | } |
| 480 | 528 | |
| 481 | 529 | /** |
| @@ -50,6 +50,8 @@ import { | ||
| 50 | 50 | unshallowCharacter, |
| 51 | 51 | deleteLastMessage, |
| 52 | 52 | getCharacterCardFields, |
| 53 | + swipe_right, | |
| 54 | + swipe_left, | |
| 53 | 55 | } from '../script.js'; |
| 54 | 56 | import { |
| 55 | 57 | extension_settings, |
| @@ -196,6 +198,7 @@ export function getContext() { | ||
| 196 | 198 | humanizedDateTime, |
| 197 | 199 | updateMessageBlock, |
| 198 | 200 | appendMediaToMessage, |
| 201 | + swipe: { left: swipe_left, right: swipe_right }, | |
| 199 | 202 | variables: { |
| 200 | 203 | local: { |
| 201 | 204 | get: getLocalVariable, |