mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2b3055a84a | ||
|
6c8bd06308 | ||
|
74d627f674 | ||
|
4fdc533bd7 | ||
|
ab460199ab | ||
|
5b63d0ff40 | ||
|
a96aad6073 | ||
|
3830347d81 | ||
|
fe813d5469 |
2
.github/readme-zh_cn.md
vendored
2
.github/readme-zh_cn.md
vendored
@@ -41,8 +41,6 @@ SillyTavern 本身并无用处,因为它只是一个用户聊天界面。你
|
||||
|
||||
<https://rentry.org/STAI-Termux>
|
||||
|
||||
Termux 不支持**.Webp 字符卡的导入/导出。请使用 JSON 或 PNG 格式**。
|
||||
|
||||
## 有问题或建议?
|
||||
|
||||
### 我们现在有了 Discord 社区
|
||||
|
3
.github/readme.md
vendored
3
.github/readme.md
vendored
@@ -41,8 +41,6 @@ Since Tavern is only a user interface, it has tiny hardware requirements, it wil
|
||||
|
||||
<https://rentry.org/STAI-Termux>
|
||||
|
||||
**.webp character cards import/export is not supported in Termux. Use either JSON or PNG formats instead.**
|
||||
|
||||
## Questions or suggestions?
|
||||
|
||||
### We now have a community Discord server
|
||||
@@ -71,7 +69,6 @@ Get in touch with the developers directly:
|
||||
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
|
||||
* [AI Horde](https://horde.koboldai.net/) connection
|
||||
* Prompt generation formatting tweaking
|
||||
* webp character card interoperability (PNG is still an internal format)
|
||||
|
||||
## Extensions
|
||||
|
||||
|
641
Launcher.bat
641
Launcher.bat
@@ -1,641 +0,0 @@
|
||||
@echo off
|
||||
REM --------------------------------------------
|
||||
REM This script was created by: Deffcolony
|
||||
REM --------------------------------------------
|
||||
title SillyTavern Launcher
|
||||
setlocal
|
||||
|
||||
REM ANSI Escape Code for Colors
|
||||
set "reset=[0m"
|
||||
|
||||
REM Strong Foreground Colors
|
||||
set "white_fg_strong=[90m"
|
||||
set "red_fg_strong=[91m"
|
||||
set "green_fg_strong=[92m"
|
||||
set "yellow_fg_strong=[93m"
|
||||
set "blue_fg_strong=[94m"
|
||||
set "magenta_fg_strong=[95m"
|
||||
set "cyan_fg_strong=[96m"
|
||||
|
||||
REM Normal Background Colors
|
||||
set "red_bg=[41m"
|
||||
set "blue_bg=[44m"
|
||||
|
||||
REM Environment Variables (TOOLBOX 7-Zip)
|
||||
set "zip7version=7z2301-x64"
|
||||
set "zip7_install_path=%ProgramFiles%\7-Zip"
|
||||
set "zip7_download_path=%TEMP%\%zip7version%.exe"
|
||||
|
||||
REM Environment Variables (TOOLBOX FFmpeg)
|
||||
set "ffmpeg_url=https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z"
|
||||
set "ffdownload_path=%TEMP%\ffmpeg.7z"
|
||||
set "ffextract_path=C:\ffmpeg"
|
||||
set "bin_path=%ffextract_path%\bin"
|
||||
|
||||
REM Environment Variables (TOOLBOX Node.js)
|
||||
set "node_installer_path=%temp%\NodejsInstaller.msi"
|
||||
|
||||
REM Environment Variables (winget)
|
||||
set "winget_path=%userprofile%\AppData\Local\Microsoft\WindowsApps"
|
||||
|
||||
REM Environment Variables (TOOLBOX Install Extras)
|
||||
set "miniconda_path=%userprofile%\miniconda"
|
||||
|
||||
|
||||
REM Check if Winget is installed; if not, then install it
|
||||
winget --version > nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo %yellow_fg_strong%[WARN] Winget is not installed on this system.
|
||||
echo %blue_fg_strong%[INFO]%reset% Installing Winget...
|
||||
bitsadmin /transfer "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe" /download /priority FOREGROUND "https://github.com/microsoft/winget-cli/releases/download/v1.5.2201/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" "%temp%\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
|
||||
start "" "%temp%\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
|
||||
echo %green_fg_strong%Winget is now installed.%reset%
|
||||
) else (
|
||||
echo %blue_fg_strong%[INFO] Winget is already installed.%reset%
|
||||
)
|
||||
|
||||
rem Get the current PATH value from the registry
|
||||
for /f "tokens=2*" %%A in ('reg query "HKCU\Environment" /v PATH') do set "current_path=%%B"
|
||||
|
||||
rem Check if the paths are already in the current PATH
|
||||
echo %current_path% | find /i "%winget_path%" > nul
|
||||
set "ff_path_exists=%errorlevel%"
|
||||
|
||||
rem Append the new paths to the current PATH only if they don't exist
|
||||
if %ff_path_exists% neq 0 (
|
||||
set "new_path=%current_path%;%winget_path%"
|
||||
|
||||
rem Update the PATH value in the registry
|
||||
reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%new_path%" /f
|
||||
|
||||
rem Update the PATH value for the current session
|
||||
setx PATH "%new_path%" > nul
|
||||
echo %green_fg_strong%winget added to PATH.%reset%
|
||||
) else (
|
||||
set "new_path=%current_path%"
|
||||
echo %blue_fg_strong%[INFO] winget already exists in PATH.%reset%
|
||||
)
|
||||
|
||||
|
||||
REM Check if Git is installed if not then install git
|
||||
git --version > nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo %yellow_fg_strong%[WARN] Git is not installed on this system.%reset%
|
||||
echo %blue_fg_strong%[INFO]%reset% Installing Git using Winget...
|
||||
winget install -e --id Git.Git
|
||||
echo %green_fg_strong%Git is installed. Please restart the Launcher.%reset%
|
||||
pause
|
||||
exit
|
||||
) else (
|
||||
echo %blue_fg_strong%[INFO] Git is already installed.%reset%
|
||||
)
|
||||
|
||||
REM Check for updates
|
||||
git fetch origin
|
||||
|
||||
for /f %%i in ('git rev-list HEAD...origin/%current_branch%') do (
|
||||
set "update_status=%yellow_fg_strong%Update Available%reset%"
|
||||
goto :found_update
|
||||
)
|
||||
|
||||
set "update_status=%green_fg_strong%Up to Date%reset%"
|
||||
:found_update
|
||||
|
||||
|
||||
REM Home - frontend
|
||||
:home
|
||||
cls
|
||||
echo %blue_fg_strong%/ Home%reset%
|
||||
echo -------------------------------------
|
||||
echo What would you like to do?
|
||||
echo 1. Start SillyTavern
|
||||
echo 2. Start SillyTavern + Extras
|
||||
echo 3. Update
|
||||
echo 4. Backup
|
||||
echo 5. Switch branch
|
||||
echo 6. Toolbox
|
||||
echo 7. Exit
|
||||
|
||||
REM Get the current Git branch
|
||||
for /f %%i in ('git branch --show-current') do set current_branch=%%i
|
||||
echo ======== VERSION STATUS =========
|
||||
echo SillyTavern branch: %cyan_fg_strong%%current_branch%%reset%
|
||||
echo Update Status: %update_status%
|
||||
echo =================================
|
||||
|
||||
set "choice="
|
||||
set /p "choice=Choose Your Destiny (default is 1): "
|
||||
|
||||
REM Default to choice 1 if no input is provided
|
||||
if not defined choice set "choice=1"
|
||||
|
||||
REM Home - backend
|
||||
if "%choice%"=="1" (
|
||||
call :start
|
||||
) else if "%choice%"=="2" (
|
||||
call :start_extras
|
||||
) else if "%choice%"=="3" (
|
||||
call :update
|
||||
) else if "%choice%"=="4" (
|
||||
call :backup_menu
|
||||
) else if "%choice%"=="5" (
|
||||
call :switchbrance_menu
|
||||
) else if "%choice%"=="6" (
|
||||
call :toolbox
|
||||
) else if "%choice%"=="7" (
|
||||
exit
|
||||
) else (
|
||||
color 6
|
||||
echo WARNING: Invalid number. Please insert a valid number.
|
||||
pause
|
||||
goto :home
|
||||
)
|
||||
|
||||
|
||||
:start
|
||||
REM Check if Node.js is installed
|
||||
node --version > nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo %red_fg_strong%[ERROR] node command not found in PATH%reset%
|
||||
echo %red_bg%Please make sure Node.js is installed and added to your PATH.%reset%
|
||||
echo %blue_bg%To install Node.js go to Toolbox%reset%
|
||||
pause
|
||||
goto :home
|
||||
)
|
||||
echo %blue_fg_strong%[INFO]%reset% A new window has been launched.
|
||||
start /wait cmd /c start.bat
|
||||
goto :home
|
||||
|
||||
|
||||
:start_extras
|
||||
REM Run conda activate from the Miniconda installation
|
||||
call "%miniconda_path%\Scripts\activate.bat"
|
||||
|
||||
REM Activate the sillytavernextras environment
|
||||
call conda activate sillytavernextras
|
||||
|
||||
REM Start SillyTavern Extras with desired configurations
|
||||
python server.py --coqui-gpu --rvc-save-file --cuda-device=0 --max-content-length=1000 --enable-modules=caption,summarize,classify,rvc,coqui-tts --classification-model=joeddav/distilbert-base-uncased-go-emotions-student --share
|
||||
goto :home
|
||||
|
||||
|
||||
:update
|
||||
echo Updating...
|
||||
pushd %~dp0
|
||||
REM Check if git is installed
|
||||
git --version > nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo %red_fg_strong%[ERROR] git command not found in PATH. Skipping update.%reset%
|
||||
echo %red_bg%Please make sure Git is installed and added to your PATH.%reset%
|
||||
echo %blue_bg%To install Git go to Toolbox%reset%
|
||||
) else (
|
||||
call git pull --rebase --autostash
|
||||
if %errorlevel% neq 0 (
|
||||
REM incase there is still something wrong
|
||||
echo There were errors while updating. Please download the latest version manually.
|
||||
)
|
||||
)
|
||||
pause
|
||||
goto :home
|
||||
|
||||
|
||||
REM Switch Brance - frontend
|
||||
:switchbrance_menu
|
||||
cls
|
||||
echo %blue_fg_strong%/ Home / Switch Branch%reset%
|
||||
echo -------------------------------------
|
||||
echo What would you like to do?
|
||||
echo 1. Switch to Release - SillyTavern
|
||||
echo 2. Switch to Staging - SillyTavern
|
||||
echo 3. Switch to Main - Extras
|
||||
echo 4. Switch to Neo - Extras
|
||||
echo 5. Back to Home
|
||||
|
||||
REM Get the current Git branch
|
||||
for /f %%i in ('git branch --show-current') do set current_branch=%%i
|
||||
echo ======== VERSION STATUS =========
|
||||
echo SillyTavern branch: %cyan_fg_strong%%current_branch%%reset%
|
||||
echo Extras branch: %cyan_fg_strong%%current_branch%%reset%
|
||||
echo =================================
|
||||
set /p brance_choice=Choose Your Destiny:
|
||||
|
||||
REM Switch Brance - backend
|
||||
if "%brance_choice%"=="1" (
|
||||
call :switch_release_st
|
||||
) else if "%brance_choice%"=="2" (
|
||||
call :switch_staging_st
|
||||
) else if "%brance_choice%"=="3" (
|
||||
call :switch_main_ste
|
||||
) else if "%brance_choice%"=="4" (
|
||||
call :switch_neo_ste
|
||||
) else if "%brance_choice%"=="5" (
|
||||
goto :home
|
||||
) else (
|
||||
color 6
|
||||
echo WARNING: Invalid number. Please insert a valid number.
|
||||
pause
|
||||
goto :switchbrance_menu
|
||||
)
|
||||
|
||||
|
||||
:switch_release_st
|
||||
echo %blue_fg_strong%[INFO]%reset% Switching to release branch...
|
||||
git switch release
|
||||
pause
|
||||
goto :switchbrance_menu
|
||||
|
||||
|
||||
:switch_staging_st
|
||||
echo %blue_fg_strong%[INFO]%reset% Switching to staging branch...
|
||||
git switch staging
|
||||
pause
|
||||
goto :switchbrance_menu
|
||||
|
||||
|
||||
:switch_main_ste
|
||||
echo %blue_fg_strong%[INFO]%reset% Switching to main branch...
|
||||
cd SillyTavern-extras
|
||||
git switch main
|
||||
pause
|
||||
goto :switchbrance_menu
|
||||
|
||||
|
||||
:switch_neo_ste
|
||||
echo %blue_fg_strong%[INFO]%reset% Switching to neo branch...
|
||||
cd SillyTavern-extras
|
||||
git switch neo
|
||||
pause
|
||||
goto :switchbrance_menu
|
||||
|
||||
|
||||
REM Backup - Frontend
|
||||
:backup_menu
|
||||
REM Check if 7-Zip is installed
|
||||
7z > nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo %red_fg_strong%[ERROR] 7z command not found in PATH%reset%
|
||||
echo %red_bg%Please make sure 7-Zip is installed and added to your PATH.%reset%
|
||||
echo %blue_bg%To install 7-Zip go to Toolbox%reset%
|
||||
pause
|
||||
goto :home
|
||||
)
|
||||
cls
|
||||
echo %blue_fg_strong%/ Home / Backup%reset%
|
||||
echo -------------------------------------
|
||||
echo What would you like to do?
|
||||
REM color 7
|
||||
echo 1. Create Backup
|
||||
echo 2. Restore Backup
|
||||
echo 3. Back to Home
|
||||
|
||||
set /p backup_choice=Choose Your Destiny:
|
||||
|
||||
REM Backup - Backend
|
||||
if "%backup_choice%"=="1" (
|
||||
call :create_backup
|
||||
) else if "%backup_choice%"=="2" (
|
||||
call :restore_backup
|
||||
) else if "%backup_choice%"=="3" (
|
||||
goto :home
|
||||
) else (
|
||||
color 6
|
||||
echo WARNING: Invalid number. Please insert a valid number.
|
||||
pause
|
||||
goto :backup_menu
|
||||
)
|
||||
|
||||
:create_backup
|
||||
REM Create a backup using 7zip
|
||||
7z a "backups\backup_.7z" ^
|
||||
"public\assets\*" ^
|
||||
"public\Backgrounds\*" ^
|
||||
"public\Characters\*" ^
|
||||
"public\Chats\*" ^
|
||||
"public\context\*" ^
|
||||
"public\Group chats\*" ^
|
||||
"public\Groups\*" ^
|
||||
"public\instruct\*" ^
|
||||
"public\KoboldAI Settings\*" ^
|
||||
"public\movingUI\*" ^
|
||||
"public\NovelAI Settings\*" ^
|
||||
"public\OpenAI Settings\*" ^
|
||||
"public\QuickReplies\*" ^
|
||||
"public\TextGen Settings\*" ^
|
||||
"public\themes\*" ^
|
||||
"public\User Avatars\*" ^
|
||||
"public\user\*" ^
|
||||
"public\worlds\*" ^
|
||||
"public\settings.json" ^
|
||||
"secrets.json"
|
||||
|
||||
REM Get current date and time components
|
||||
for /f "tokens=1-3 delims=/- " %%d in ("%date%") do (
|
||||
set "day=%%d"
|
||||
set "month=%%e"
|
||||
set "year=%%f"
|
||||
)
|
||||
|
||||
for /f "tokens=1-2 delims=:." %%h in ("%time%") do (
|
||||
set "hour=%%h"
|
||||
set "minute=%%i"
|
||||
)
|
||||
|
||||
REM Pad single digits with leading zeros
|
||||
setlocal enabledelayedexpansion
|
||||
set "day=0!day!"
|
||||
set "month=0!month!"
|
||||
set "hour=0!hour!"
|
||||
set "minute=0!minute!"
|
||||
|
||||
set "formatted_date=%month:~-2%-%day:~-2%-%year%_%hour:~-2%%minute:~-2%"
|
||||
|
||||
REM Rename the backup file with the formatted date and time
|
||||
rename "backups\backup_.7z" "backup_%formatted_date%.7z"
|
||||
|
||||
endlocal
|
||||
|
||||
|
||||
echo %green_fg_strong%Backup created successfully!%reset%
|
||||
pause
|
||||
endlocal
|
||||
goto :backup_menu
|
||||
|
||||
|
||||
:restore_backup
|
||||
REM Restore a backup using 7zip
|
||||
|
||||
echo List of available backups:
|
||||
echo =========================
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
set "backup_count=0"
|
||||
|
||||
for %%F in ("backups\backup_*.7z") do (
|
||||
set /a "backup_count+=1"
|
||||
set "backup_files[!backup_count!]=%%~nF"
|
||||
echo !backup_count!. %cyan_fg_strong%%%~nF%reset%
|
||||
)
|
||||
|
||||
echo =========================
|
||||
set /p "restore_choice=Enter number of backup to restore: "
|
||||
|
||||
if "%restore_choice%" geq "1" (
|
||||
if "%restore_choice%" leq "%backup_count%" (
|
||||
set "selected_backup=!backup_files[%restore_choice%]!"
|
||||
echo Restoring backup !selected_backup!...
|
||||
REM Extract the contents of the "public" folder directly into the existing "public" folder
|
||||
7z x "backups\!selected_backup!.7z" -o"temp" -aoa
|
||||
xcopy /y /e "temp\public\*" "public\"
|
||||
rmdir /s /q "temp"
|
||||
echo %green_fg_strong%!selected_backup! restored successfully.%reset%
|
||||
) else (
|
||||
color 6
|
||||
echo WARNING: Invalid backup number. Please insert a valid number.
|
||||
)
|
||||
) else (
|
||||
color 6
|
||||
echo WARNING: Invalid number. Please insert a valid number.
|
||||
)
|
||||
pause
|
||||
goto :backup_menu
|
||||
|
||||
|
||||
REM Toolbox - Frontend
|
||||
:toolbox
|
||||
cls
|
||||
echo %blue_fg_strong%/ Home / Toolbox%reset%
|
||||
echo -------------------------------------
|
||||
echo What would you like to do?
|
||||
REM color 7
|
||||
echo 1. Install 7-Zip
|
||||
echo 2. Install FFmpeg
|
||||
echo 3. Install Node.js
|
||||
echo 4. Edit Environment
|
||||
echo 5. Reinstall SillyTavern
|
||||
echo 6. Reinstall Extras
|
||||
echo 7. Back to Home
|
||||
|
||||
set /p toolbox_choice=Choose Your Destiny:
|
||||
|
||||
REM Toolbox - Backend
|
||||
if "%toolbox_choice%"=="1" (
|
||||
call :install7zip
|
||||
) else if "%toolbox_choice%"=="2" (
|
||||
call :installffmpeg
|
||||
) else if "%toolbox_choice%"=="3" (
|
||||
call :installnodejs
|
||||
) else if "%toolbox_choice%"=="4" (
|
||||
call :editenvironment
|
||||
) else if "%toolbox_choice%"=="5" (
|
||||
call :reinstallsillytavern
|
||||
) else if "%toolbox_choice%"=="6" (
|
||||
call :reinstallextras
|
||||
) else if "%toolbox_choice%"=="7" (
|
||||
goto :home
|
||||
) else (
|
||||
color 6
|
||||
echo WARNING: Invalid number. Please insert a valid number.
|
||||
pause
|
||||
goto :toolbox
|
||||
)
|
||||
|
||||
|
||||
:install7zip
|
||||
echo %blue_fg_strong%[INFO]%reset% Installing 7-Zip...
|
||||
winget install -e --id 7zip.7zip
|
||||
|
||||
rem Get the current PATH value from the registry
|
||||
for /f "tokens=2*" %%A in ('reg query "HKCU\Environment" /v PATH') do set "current_path=%%B"
|
||||
|
||||
rem Check if the paths are already in the current PATH
|
||||
echo %current_path% | find /i "%zip7_install_path%" > nul
|
||||
set "zip7_path_exists=%errorlevel%"
|
||||
|
||||
rem Append the new paths to the current PATH only if they don't exist
|
||||
if %zip7_path_exists% neq 0 (
|
||||
set "new_path=%current_path%;%zip7_install_path%"
|
||||
echo %green_fg_strong%7-Zip added to PATH.%reset%
|
||||
) else (
|
||||
set "new_path=%current_path%"
|
||||
echo %blue_fg_strong%[INFO] 7-Zip already exists in PATH.%reset%
|
||||
)
|
||||
|
||||
rem Update the PATH value in the registry
|
||||
reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%new_path%" /f
|
||||
|
||||
rem Update the PATH value for the current session
|
||||
setx PATH "%new_path%"
|
||||
|
||||
echo %green_fg_strong%7-Zip is installed. Please restart the Launcher.%reset%
|
||||
pause
|
||||
exit
|
||||
|
||||
|
||||
:installffmpeg
|
||||
REM Check if 7-Zip is installed
|
||||
7z > nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo %red_fg_strong%[ERROR] 7z command not found in PATH%reset%
|
||||
echo %red_bg%Please make sure 7-Zip is installed and added to your PATH.%reset%
|
||||
echo %blue_bg%To install 7-Zip go to Toolbox%reset%
|
||||
pause
|
||||
goto :toolbox
|
||||
)
|
||||
|
||||
echo %blue_fg_strong%[INFO]%reset% Downloading FFmpeg archive...
|
||||
rem bitsadmin /transfer "ffmpeg" /download /priority FOREGROUND "%ffmpeg_url%" "%ffdownload_path%"
|
||||
curl -o "%ffdownload_path%" "%ffmpeg_url%"
|
||||
|
||||
echo %blue_fg_strong%[INFO]%reset% Creating ffmpeg directory if it doesn't exist...
|
||||
if not exist "%ffextract_path%" (
|
||||
mkdir "%ffextract_path%"
|
||||
)
|
||||
|
||||
echo %blue_fg_strong%[INFO]%reset% Extracting FFmpeg archive...
|
||||
7z x "%ffdownload_path%" -o"%ffextract_path%"
|
||||
|
||||
|
||||
echo %blue_fg_strong%[INFO]%reset% Moving FFmpeg contents to C:\ffmpeg...
|
||||
for /d %%i in ("%ffextract_path%\ffmpeg-*-full_build") do (
|
||||
xcopy "%%i\bin" "%ffextract_path%\bin" /E /I /Y
|
||||
xcopy "%%i\doc" "%ffextract_path%\doc" /E /I /Y
|
||||
xcopy "%%i\presets" "%ffextract_path%\presets" /E /I /Y
|
||||
rd "%%i" /S /Q
|
||||
)
|
||||
|
||||
rem Get the current PATH value from the registry
|
||||
for /f "tokens=2*" %%A in ('reg query "HKCU\Environment" /v PATH') do set "current_path=%%B"
|
||||
|
||||
rem Check if the paths are already in the current PATH
|
||||
echo %current_path% | find /i "%bin_path%" > nul
|
||||
set "ff_path_exists=%errorlevel%"
|
||||
|
||||
rem Append the new paths to the current PATH only if they don't exist
|
||||
if %ff_path_exists% neq 0 (
|
||||
set "new_path=%current_path%;%bin_path%"
|
||||
echo %green_fg_strong%ffmpeg added to PATH.%reset%
|
||||
) else (
|
||||
set "new_path=%current_path%"
|
||||
echo %blue_fg_strong%[INFO] ffmpeg already exists in PATH.%reset%
|
||||
)
|
||||
|
||||
rem Update the PATH value in the registry
|
||||
reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%new_path%" /f
|
||||
|
||||
rem Update the PATH value for the current session
|
||||
setx PATH "%new_path%" > nul
|
||||
|
||||
del "%ffdownload_path%"
|
||||
echo %green_fg_strong%FFmpeg is installed. Please restart the Launcher.%reset%
|
||||
pause
|
||||
exit
|
||||
|
||||
|
||||
:installnodejs
|
||||
echo %blue_fg_strong%[INFO]%reset% Installing Node.js...
|
||||
winget install -e --id OpenJS.NodeJS
|
||||
echo %green_fg_strong%Node.js is installed. Please restart the Launcher.%reset%
|
||||
pause
|
||||
exit
|
||||
|
||||
:editenvironment
|
||||
rundll32.exe sysdm.cpl,EditEnvironmentVariables
|
||||
goto :toolbox
|
||||
|
||||
:reinstallsillytavern
|
||||
setlocal enabledelayedexpansion
|
||||
chcp 65001 > nul
|
||||
REM Define the names of items to be excluded
|
||||
set "script_name=%~nx0"
|
||||
set "excluded_folders=backups"
|
||||
set "excluded_files=!script_name!"
|
||||
|
||||
REM Confirm with the user before proceeding
|
||||
echo.
|
||||
echo %red_bg%╔════ DANGER ZONE ══════════════════════════════════════════════════════════════════════════════╗%reset%
|
||||
echo %red_bg%║ WARNING: This will delete all data in the current branch except the Backups. ║%reset%
|
||||
echo %red_bg%║ If you want to keep any data, make sure to create a backup before proceeding. ║%reset%
|
||||
echo %red_bg%╚═══════════════════════════════════════════════════════════════════════════════════════════════╝%reset%
|
||||
echo.
|
||||
echo Are you sure you want to proceed? [Y/N]
|
||||
set /p "confirmation="
|
||||
if /i "!confirmation!"=="Y" (
|
||||
REM Remove non-excluded folders
|
||||
for /d %%D in (*) do (
|
||||
set "exclude_folder="
|
||||
for %%E in (!excluded_folders!) do (
|
||||
if "%%D"=="%%E" set "exclude_folder=true"
|
||||
)
|
||||
if not defined exclude_folder (
|
||||
rmdir /s /q "%%D" 2>nul
|
||||
)
|
||||
)
|
||||
|
||||
REM Remove non-excluded files
|
||||
for %%F in (*) do (
|
||||
set "exclude_file="
|
||||
for %%E in (!excluded_files!) do (
|
||||
if "%%F"=="%%E" set "exclude_file=true"
|
||||
)
|
||||
if not defined exclude_file (
|
||||
del /f /q "%%F" 2>nul
|
||||
)
|
||||
)
|
||||
|
||||
REM Clone repo into %temp% folder
|
||||
git clone https://github.com/SillyTavern/SillyTavern.git "%temp%\SillyTavernTemp"
|
||||
|
||||
REM Move the contents of the temporary folder to the current directory
|
||||
xcopy /e /y "%temp%\SillyTavernTemp\*" .
|
||||
|
||||
REM Clean up the temporary folder
|
||||
rmdir /s /q "%temp%\SillyTavernTemp"
|
||||
|
||||
echo %green_fg_strong%SillyTavern reinstalled successfully!%reset%
|
||||
) else (
|
||||
echo Reinstall canceled.
|
||||
)
|
||||
endlocal
|
||||
pause
|
||||
goto :toolbox
|
||||
|
||||
|
||||
:reinstallextras
|
||||
cls
|
||||
echo %blue_fg_strong%SillyTavern Extras%reset%
|
||||
echo ---------------------------------------------------------------
|
||||
echo %blue_fg_strong%[INFO]%reset% Installing SillyTavern Extras...
|
||||
echo --------------------------------
|
||||
echo %cyan_fg_strong%This may take a while. Please be patient.%reset%
|
||||
|
||||
winget install -e --id Anaconda.Miniconda3
|
||||
|
||||
REM Run conda activate from the Miniconda installation
|
||||
call "%miniconda_path%\Scripts\activate.bat"
|
||||
|
||||
REM Create a Conda environment named sillytavernextras
|
||||
call conda create -n sillytavernextras -y
|
||||
|
||||
REM Activate the sillytavernextras environment
|
||||
call conda activate sillytavernextras
|
||||
|
||||
REM Install Python 3.11 and Git in the sillytavernextras environment
|
||||
call conda install python=3.11 git -y
|
||||
|
||||
REM Clone the SillyTavern Extras repository
|
||||
git clone https://github.com/SillyTavern/SillyTavern-extras
|
||||
|
||||
REM Navigate to the SillyTavern-extras directory
|
||||
cd SillyTavern-extras
|
||||
|
||||
REM Install Python dependencies from requirements files
|
||||
pip install -r requirements-complete.txt
|
||||
pip install -r requirements-rvc.txt
|
||||
|
||||
REM Start SillyTavern Extras with desired configurations
|
||||
python server.py --coqui-gpu --rvc-save-file --cuda-device=0 --max-content-length=1000 --enable-modules=caption,summarize,classify,rvc,coqui-tts --classification-model=joeddav/distilbert-base-uncased-go-emotions-student --share
|
||||
|
||||
echo.
|
||||
echo %green_fg_strong%SillyTavern Extras have been successfully installed.%reset%
|
||||
pause
|
||||
goto :toolbox
|
35
package-lock.json
generated
35
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.10.1",
|
||||
"version": "1.10.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.10.1",
|
||||
"version": "1.10.3",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@agnai/sentencepiece-js": "^1.1.1",
|
||||
@@ -18,7 +18,6 @@
|
||||
"cors": "^2.8.5",
|
||||
"csrf-csrf": "^2.2.3",
|
||||
"device-detector-js": "^3.0.3",
|
||||
"exifreader": "^4.12.0",
|
||||
"express": "^4.18.2",
|
||||
"google-translate-api-browser": "^3.0.1",
|
||||
"gpt3-tokenizer": "^1.1.5",
|
||||
@@ -32,7 +31,6 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-fetch": "^2.6.11",
|
||||
"open": "^8.4.2",
|
||||
"piexifjs": "^1.0.6",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
@@ -40,7 +38,6 @@
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"simple-git": "^3.19.1",
|
||||
"uniqolor": "^1.1.0",
|
||||
"webp-converter": "2.3.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.13.0",
|
||||
"yargs": "^17.7.1",
|
||||
@@ -652,15 +649,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
|
||||
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.9",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.9.tgz",
|
||||
"integrity": "sha512-4VSbbcMoxc4KLjb1gs96SRmi7w4h1SF+fCoiK0XaQX62buCc1G5d0DC5bJ9xJBNPDSVCmIrcl8BiYxzjrqaaJA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -1281,15 +1269,6 @@
|
||||
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
|
||||
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
|
||||
},
|
||||
"node_modules/exifreader": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/exifreader/-/exifreader-4.13.0.tgz",
|
||||
"integrity": "sha512-IhJBpyXDLbCdgzVHkthadOvrMiZOR2XS7POVp0b5JoVfScRoCJ6YazZ+stTkbDTE5TRTP44bE5RKsujckAs45Q==",
|
||||
"hasInstallScript": true,
|
||||
"optionalDependencies": {
|
||||
"@xmldom/xmldom": "^0.8.8"
|
||||
}
|
||||
},
|
||||
"node_modules/expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
@@ -2381,11 +2360,6 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/piexifjs": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/piexifjs/-/piexifjs-1.0.6.tgz",
|
||||
"integrity": "sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag=="
|
||||
},
|
||||
"node_modules/pixelmatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
|
||||
@@ -3385,11 +3359,6 @@
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/webp-converter": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.3.2.tgz",
|
||||
"integrity": "sha512-9kQ9Q/MPzUV2mye8Tv7vA6vDIPk77rI4AWWm2vSaCyGAEsxqyVZYeVU2MSJY5fLkf6u7G5K343vLxKubOxz16Q=="
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
|
@@ -9,7 +9,6 @@
|
||||
"cors": "^2.8.5",
|
||||
"csrf-csrf": "^2.2.3",
|
||||
"device-detector-js": "^3.0.3",
|
||||
"exifreader": "^4.12.0",
|
||||
"express": "^4.18.2",
|
||||
"google-translate-api-browser": "^3.0.1",
|
||||
"gpt3-tokenizer": "^1.1.5",
|
||||
@@ -23,7 +22,6 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-fetch": "^2.6.11",
|
||||
"open": "^8.4.2",
|
||||
"piexifjs": "^1.0.6",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
@@ -31,7 +29,6 @@
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"simple-git": "^3.19.1",
|
||||
"uniqolor": "^1.1.0",
|
||||
"webp-converter": "2.3.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.13.0",
|
||||
"yargs": "^17.7.1",
|
||||
@@ -49,7 +46,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.10.1",
|
||||
"version": "1.10.3",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start-multi": "node server.js --disableCsrf",
|
||||
|
@@ -284,7 +284,6 @@
|
||||
"Regenerate": "重新生成",
|
||||
"PNG": "PNG",
|
||||
"JSON": "JSON",
|
||||
"WEBP": "WEBP",
|
||||
"presets": "预设",
|
||||
"Message Sound": "AI 消息提示音",
|
||||
"Author's Note": "作者注释",
|
||||
@@ -836,7 +835,6 @@
|
||||
"Regenerate": "再生成",
|
||||
"PNG": "PNG",
|
||||
"JSON": "JSON",
|
||||
"WEBP": "WEBP",
|
||||
"presets": "プリセット",
|
||||
"Message Sound": "メッセージ音",
|
||||
"Author's Note": "作者の注記",
|
||||
@@ -1392,7 +1390,6 @@
|
||||
"Regenerate": "재생성",
|
||||
"PNG": "PNG",
|
||||
"JSON": "JSON",
|
||||
"WEBP": "WEBP",
|
||||
"presets": "기본설정",
|
||||
"Message Sound": "메시지 효과음",
|
||||
"Author's Note": "글쓴이 쪽지",
|
||||
@@ -2016,7 +2013,6 @@
|
||||
"Regenerate": "Повторная генерация",
|
||||
"PNG": "PNG",
|
||||
"JSON": "JSON",
|
||||
"WEBP": "WEBP",
|
||||
"presets": "Предустановки",
|
||||
"Message Sound": "Звук сообщения",
|
||||
"Author's Note": "Авторские заметки",
|
||||
@@ -2580,7 +2576,6 @@
|
||||
"Regenerate": "Rigenera",
|
||||
"PNG": "PNG",
|
||||
"JSON": "JSON",
|
||||
"WEBP": "WEBP",
|
||||
"presets": "preset",
|
||||
"Message Sound": "Suono del messaggio",
|
||||
"Author's Note": "Note d'autore",
|
||||
@@ -3259,7 +3254,6 @@
|
||||
"Regenerate": "Regenereren",
|
||||
"PNG": "PNG",
|
||||
"JSON": "JSON",
|
||||
"WEBP": "WEBP",
|
||||
"presets": "sjablonen",
|
||||
"Message Sound": "Berichtgeluid",
|
||||
"Author's Note": "Notitie van auteur",
|
||||
|
@@ -3482,7 +3482,7 @@
|
||||
|
||||
<div id="rm_character_import" class="right_menu" style="display: none;">
|
||||
<form id="form_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<input multiple type="file" id="character_import_file" accept=".json, image/png, image/webp" name="avatar">
|
||||
<input multiple type="file" id="character_import_file" accept=".json, image/png" name="avatar">
|
||||
<input id="character_import_file_type" name="file_type" class="text_pole" maxlength="999" size="2" value="" autocomplete="off">
|
||||
</form>
|
||||
</div>
|
||||
@@ -4445,7 +4445,6 @@
|
||||
<div id="export_format_popup" class="list-group">
|
||||
<div class="export_format list-group-item" data-format="png">PNG</div>
|
||||
<div class="export_format list-group-item" data-format="json">JSON</div>
|
||||
<div class="export_format list-group-item" data-format="webp">WEBP</div>
|
||||
</div>
|
||||
|
||||
<div id="zoomed_avatar_template" class="template_element">
|
||||
|
@@ -5410,17 +5410,18 @@ function select_rm_info(type, charId, previousCharId = null) {
|
||||
$('#rm_print_characters_pagination').pagination('go', page);
|
||||
|
||||
waitUntilCondition(() => document.querySelector(selector) !== null).then(() => {
|
||||
const element = $(selector).parent().get(0);
|
||||
const parent = $('#rm_print_characters_block');
|
||||
const element = $(selector).parent();
|
||||
|
||||
if (!element) {
|
||||
if (element.length === 0) {
|
||||
console.log(`Could not find element for character ${charId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
$(element).addClass('flash animated');
|
||||
parent.scrollTop(element.position().top + parent.scrollTop());
|
||||
element.addClass('flash animated');
|
||||
setTimeout(function () {
|
||||
$(element).removeClass('flash animated');
|
||||
element.removeClass('flash animated');
|
||||
}, 5000);
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -5429,16 +5430,29 @@ function select_rm_info(type, charId, previousCharId = null) {
|
||||
}
|
||||
|
||||
if (type === 'group_create') {
|
||||
//for groups, ${charId} = data.id from group-chats.js createGroup()
|
||||
const element = $(`#rm_characters_block [grid="${charId}"]`).get(0);
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
// Find the page at which the character is located
|
||||
const charData = getEntitiesList({ doFilter: true });
|
||||
const charIndex = charData.findIndex((x) => String(x?.item?.id) === String(charId));
|
||||
|
||||
if (charIndex === -1) {
|
||||
console.log(`Could not find group ${charId} in the list`);
|
||||
return;
|
||||
}
|
||||
|
||||
const perPage = Number(localStorage.getItem('Characters_PerPage'));
|
||||
const page = Math.floor(charIndex / perPage) + 1;
|
||||
$('#rm_print_characters_pagination').pagination('go', page);
|
||||
const parent = $('#rm_print_characters_block');
|
||||
const selector = `#rm_print_characters_block [grid="${charId}"]`;
|
||||
try {
|
||||
if (element !== undefined || element !== null) {
|
||||
waitUntilCondition(() => document.querySelector(selector) !== null).then(() => {
|
||||
const element = $(selector);
|
||||
parent.scrollTop(element.position().top + parent.scrollTop());
|
||||
$(element).addClass('flash animated');
|
||||
setTimeout(function () {
|
||||
$(element).removeClass('flash animated');
|
||||
}, 5000);
|
||||
} else { console.log('didnt find the element'); }
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -6732,7 +6746,6 @@ export function processDroppedFiles(files) {
|
||||
const allowedMimeTypes = [
|
||||
'application/json',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
];
|
||||
|
||||
for (const file of files) {
|
||||
@@ -6748,7 +6761,7 @@ function importCharacter(file) {
|
||||
const ext = file.name.match(/\.(\w+)$/);
|
||||
if (
|
||||
!ext ||
|
||||
(ext[1].toLowerCase() != "json" && ext[1].toLowerCase() != "png" && ext[1] != "webp")
|
||||
(ext[1].toLowerCase() != "json" && ext[1].toLowerCase() != "png")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@@ -582,10 +582,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
typingIndicator
|
||||
.find(".typing_indicator_name")
|
||||
.text(characters[chId].name);
|
||||
$("#chat").append(typingIndicator);
|
||||
typingIndicator.show(200, function () {
|
||||
typingIndicator.get(0).scrollIntoView({ behavior: "smooth" });
|
||||
});
|
||||
typingIndicator.show();
|
||||
}
|
||||
|
||||
// TODO: This is awful. Refactor this
|
||||
@@ -681,9 +678,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// hide and reapply the indicator to the bottom of the list
|
||||
typingIndicator.hide(200);
|
||||
$("#chat").append(typingIndicator);
|
||||
typingIndicator.hide();
|
||||
|
||||
is_group_generating = false;
|
||||
$("#send_textarea").attr("disabled", false);
|
||||
@@ -1109,6 +1104,7 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
$("#rm_group_restore_avatar").toggle(!!group && isValidImageUrl(group.avatar_url));
|
||||
$("#rm_group_filter").val("").trigger("input");
|
||||
$(`input[name="rm_group_activation_strategy"][value="${replyStrategy}"]`).prop('checked', true);
|
||||
$("#rm_group_chat_name").val(groupName);
|
||||
|
||||
if (!skipAnimation) {
|
||||
selectRightMenuWithAnimation('rm_group_chats_block');
|
||||
|
@@ -576,6 +576,7 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
|
||||
chatCompletion.add(collection, index);
|
||||
};
|
||||
|
||||
chatCompletion.reserveBudget(3); // every reply is primed with <|start|>assistant<|message|>
|
||||
// Character and world information
|
||||
addToChatCompletion('worldInfoBefore');
|
||||
addToChatCompletion('main');
|
||||
@@ -1761,9 +1762,12 @@ class ChatCompletion {
|
||||
/**
|
||||
* Reserves the tokens required by the given message from the token budget.
|
||||
*
|
||||
* @param {Message|MessageCollection} message - The message whose tokens to reserve.
|
||||
* @param {Message|MessageCollection|number} message - The message whose tokens to reserve.
|
||||
*/
|
||||
reserveBudget(message) { this.decreaseTokenBudgetBy(message.getTokens()) };
|
||||
reserveBudget(message) {
|
||||
const tokens = typeof message === 'number' ? message : message.getTokens();
|
||||
this.decreaseTokenBudgetBy(tokens);
|
||||
};
|
||||
|
||||
/**
|
||||
* Frees up the tokens used by the given message from the token budget.
|
||||
|
@@ -2768,6 +2768,7 @@ body .ui-widget-content li:hover {
|
||||
margin: 10px;
|
||||
opacity: 0.85;
|
||||
text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor);
|
||||
order: 9999;
|
||||
}
|
||||
|
||||
.typing_indicator:after {
|
||||
@@ -3608,4 +3609,4 @@ a {
|
||||
height: 100vh;
|
||||
z-index: 9999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
120
server.js
120
server.js
@@ -32,6 +32,8 @@ const multer = require("multer");
|
||||
const responseTime = require('response-time');
|
||||
|
||||
// net related library imports
|
||||
const net = require("net");
|
||||
const dns = require('dns');
|
||||
const DeviceDetector = require("device-detector-js");
|
||||
const fetch = require('node-fetch').default;
|
||||
const ipaddr = require('ipaddr.js');
|
||||
@@ -40,13 +42,11 @@ const json5 = require('json5');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
// image processing related library imports
|
||||
const exif = require('piexifjs');
|
||||
const encode = require('png-chunks-encode');
|
||||
const extract = require('png-chunks-extract');
|
||||
const jimp = require('jimp');
|
||||
const mime = require('mime-types');
|
||||
const PNGtext = require('png-chunk-text');
|
||||
const webp = require('webp-converter');
|
||||
const yauzl = require('yauzl');
|
||||
|
||||
// tokenizing related library imports
|
||||
@@ -89,9 +89,16 @@ function createDefaultFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
const net = require("net");
|
||||
// @ts-ignore work around a node v20 bug: https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
|
||||
if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
|
||||
// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0.
|
||||
// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
|
||||
// Safe to remove once support for Node v20 is dropped.
|
||||
if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) {
|
||||
// @ts-ignore
|
||||
if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
|
||||
}
|
||||
|
||||
// Set default DNS resolution order to IPv4 first
|
||||
dns.setDefaultResultOrder('ipv4first');
|
||||
|
||||
const cliArguments = yargs(hideBin(process.argv))
|
||||
.option('disableCsrf', {
|
||||
@@ -1128,7 +1135,7 @@ app.post("/renamecharacter", jsonParser, async function (request, response) {
|
||||
try {
|
||||
// Read old file, replace name int it
|
||||
const rawOldData = await charaRead(oldAvatarPath);
|
||||
if (rawOldData === false || rawOldData === undefined) throw new Error("Failed to read character file");
|
||||
if (rawOldData === undefined) throw new Error("Failed to read character file");
|
||||
|
||||
const oldData = getCharaCardV2(json5.parse(rawOldData));
|
||||
_.set(oldData, 'data.name', newName);
|
||||
@@ -1375,7 +1382,7 @@ const calculateDataSize = (data) => {
|
||||
const processCharacter = async (item, i) => {
|
||||
try {
|
||||
const img_data = await charaRead(charactersPath + item);
|
||||
if (img_data === false || img_data === undefined) throw new Error("Failed to read character file");
|
||||
if (img_data === undefined) throw new Error("Failed to read character file");
|
||||
|
||||
let jsonObject = getCharaCardV2(json5.parse(img_data));
|
||||
jsonObject.avatar = item;
|
||||
@@ -2212,26 +2219,13 @@ app.post("/importcharacter", urlencodedParser, async function (request, response
|
||||
} else {
|
||||
try {
|
||||
var img_data = await charaRead(uploadPath, format);
|
||||
if (img_data === false || img_data === undefined) throw new Error('Failed to read character data');
|
||||
if (img_data === undefined) throw new Error('Failed to read character data');
|
||||
|
||||
let jsonData = json5.parse(img_data);
|
||||
|
||||
jsonData.name = sanitize(jsonData.data?.name || jsonData.name);
|
||||
png_name = getPngName(jsonData.name);
|
||||
|
||||
if (format == 'webp') {
|
||||
try {
|
||||
let convertedPath = path.join(UPLOADS_PATH, path.basename(uploadPath, ".webp") + ".png")
|
||||
await webp.dwebp(uploadPath, convertedPath, "-o");
|
||||
fs.unlinkSync(uploadPath);
|
||||
uploadPath = convertedPath;
|
||||
}
|
||||
catch {
|
||||
console.error('WEBP image conversion failed. Using the default character image.');
|
||||
uploadPath = defaultAvatarPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonData.spec !== undefined) {
|
||||
console.log('Found a v2 character file.');
|
||||
importRisuSprites(jsonData);
|
||||
@@ -2411,7 +2405,7 @@ app.post("/exportcharacter", jsonParser, async function (request, response) {
|
||||
case 'json': {
|
||||
try {
|
||||
let json = await charaRead(filename);
|
||||
if (json === false || json === undefined) return response.sendStatus(400);
|
||||
if (json === undefined) return response.sendStatus(400);
|
||||
let jsonObject = getCharaCardV2(json5.parse(json));
|
||||
return response.type('json').send(jsonObject)
|
||||
}
|
||||
@@ -2419,39 +2413,6 @@ app.post("/exportcharacter", jsonParser, async function (request, response) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
}
|
||||
case 'webp': {
|
||||
try {
|
||||
let json = await charaRead(filename);
|
||||
if (json === false || json === undefined) return response.sendStatus(400);
|
||||
let stringByteArray = utf8Encode.encode(json).toString();
|
||||
let inputWebpPath = path.join(UPLOADS_PATH, `${Date.now()}_input.webp`);
|
||||
let outputWebpPath = path.join(UPLOADS_PATH, `${Date.now()}_output.webp`);
|
||||
let metadataPath = path.join(UPLOADS_PATH, `${Date.now()}_metadata.exif`);
|
||||
let metadata =
|
||||
{
|
||||
"Exif": {
|
||||
[exif.ExifIFD.UserComment]: stringByteArray,
|
||||
},
|
||||
};
|
||||
const exifString = exif.dump(metadata);
|
||||
writeFileAtomicSync(metadataPath, exifString, 'binary');
|
||||
|
||||
await webp.cwebp(filename, inputWebpPath, '-q 95');
|
||||
await webp.webpmux_add(inputWebpPath, outputWebpPath, metadataPath, 'exif');
|
||||
|
||||
response.sendFile(outputWebpPath, { root: process.cwd() }, () => {
|
||||
fs.rmSync(inputWebpPath);
|
||||
fs.rmSync(metadataPath);
|
||||
fs.rmSync(outputWebpPath);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response.sendStatus(400);
|
||||
@@ -4005,8 +3966,10 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
|
||||
headers: { "Content-Type": "application/json" }
|
||||
};
|
||||
|
||||
if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
|
||||
args.headers = Object.assign(args.headers, get_mancer_headers());
|
||||
if (main_api == 'textgenerationwebui') {
|
||||
if (request.body.use_mancer) {
|
||||
args.headers = Object.assign(args.headers, get_mancer_headers());
|
||||
}
|
||||
const data = await postAsync(api_server + "/v1/token-count", args);
|
||||
return response.send({ count: data['results'][0]['tokens'] });
|
||||
}
|
||||
@@ -4078,8 +4041,6 @@ const setupTasks = async function () {
|
||||
contentManager.checkForNewContent();
|
||||
cleanUploads();
|
||||
|
||||
await convertWebp();
|
||||
|
||||
[spp_llama, spp_nerd, spp_nerd_v2, claude_tokenizer] = await Promise.all([
|
||||
loadSentencepieceTokenizer('src/sentencepiece/tokenizer.model'),
|
||||
loadSentencepieceTokenizer('src/sentencepiece/nerdstash.model'),
|
||||
@@ -4139,47 +4100,6 @@ if (true === cliArguments.ssl) {
|
||||
);
|
||||
}
|
||||
|
||||
async function convertWebp() {
|
||||
const files = fs.readdirSync(directories.characters).filter(e => e.endsWith(".webp"));
|
||||
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${files.length} WEBP files will be automatically converted.`);
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const source = path.join(directories.characters, file);
|
||||
const dest = path.join(directories.characters, path.basename(file, ".webp") + ".png");
|
||||
|
||||
if (fs.existsSync(dest)) {
|
||||
console.log(`${dest} already exists. Delete ${source} manually`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Read... ${source}`);
|
||||
const data = await charaRead(source);
|
||||
|
||||
console.log(`Convert... ${source} -> ${dest}`);
|
||||
await webp.dwebp(source, dest, "-o");
|
||||
|
||||
console.log(`Write... ${dest}`);
|
||||
const success = await charaWrite(dest, data, path.parse(dest).name);
|
||||
|
||||
if (!success) {
|
||||
console.log(`Failure on ${source} -> ${dest}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Remove... ${source}`);
|
||||
fs.rmSync(source);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function backupSettings() {
|
||||
const MAX_BACKUPS = 25;
|
||||
|
||||
|
@@ -1,56 +1,12 @@
|
||||
const fs = require('fs');
|
||||
const json5 = require('json5');
|
||||
const ExifReader = require('exifreader');
|
||||
|
||||
const extract = require('png-chunks-extract');
|
||||
const PNGtext = require('png-chunk-text');
|
||||
|
||||
const utf8Decode = new TextDecoder('utf-8', { ignoreBOM: true });
|
||||
|
||||
const parse = async (cardUrl, format) => {
|
||||
let fileFormat;
|
||||
if (format === undefined) {
|
||||
if (cardUrl.indexOf('.webp') !== -1)
|
||||
fileFormat = 'webp';
|
||||
else
|
||||
fileFormat = 'png';
|
||||
}
|
||||
else
|
||||
fileFormat = format;
|
||||
let fileFormat = format === undefined ? 'png' : format;
|
||||
|
||||
switch (fileFormat) {
|
||||
case 'webp':
|
||||
try {
|
||||
const exif_data = await ExifReader.load(fs.readFileSync(cardUrl));
|
||||
let char_data;
|
||||
|
||||
if (exif_data['UserComment']['description']) {
|
||||
let description = exif_data['UserComment']['description'];
|
||||
if (description === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) {
|
||||
description = exif_data['UserComment'].value[0];
|
||||
}
|
||||
|
||||
try {
|
||||
json5.parse(description);
|
||||
char_data = description;
|
||||
} catch {
|
||||
const byteArr = description.split(",").map(Number);
|
||||
const uint8Array = new Uint8Array(byteArr);
|
||||
const char_data_string = utf8Decode.decode(uint8Array);
|
||||
char_data = char_data_string;
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('No description found in EXIF data.');
|
||||
return false;
|
||||
}
|
||||
|
||||
return char_data;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return false;
|
||||
}
|
||||
case 'png':
|
||||
const buffer = fs.readFileSync(cardUrl);
|
||||
const chunks = extract(buffer);
|
||||
|
@@ -1,117 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import jimp from 'jimp';
|
||||
import extract from 'png-chunks-extract';
|
||||
import encode from 'png-chunks-encode';
|
||||
import PNGtext from 'png-chunk-text';
|
||||
import ExifReader from 'exifreader';
|
||||
import webp from 'webp-converter';
|
||||
import path from 'path';
|
||||
|
||||
async function charaRead(img_url, input_format){
|
||||
let format;
|
||||
if(input_format === undefined){
|
||||
if(img_url.indexOf('.webp') !== -1){
|
||||
format = 'webp';
|
||||
}else{
|
||||
format = 'png';
|
||||
}
|
||||
}else{
|
||||
format = input_format;
|
||||
}
|
||||
|
||||
switch(format){
|
||||
case 'webp':
|
||||
const exif_data = await ExifReader.load(fs.readFileSync(img_url));
|
||||
const char_data = exif_data['UserComment']['description'];
|
||||
if (char_data === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) {
|
||||
return exif_data['UserComment'].value[0];
|
||||
}
|
||||
return char_data;
|
||||
case 'png':
|
||||
const buffer = fs.readFileSync(img_url);
|
||||
const chunks = extract(buffer);
|
||||
|
||||
const textChunks = chunks.filter(function (chunk) {
|
||||
return chunk.name === 'tEXt';
|
||||
}).map(function (chunk) {
|
||||
//console.log(text.decode(chunk.data));
|
||||
return PNGtext.decode(chunk.data);
|
||||
});
|
||||
var base64DecodedData = Buffer.from(textChunks[0].text, 'base64').toString('utf8');
|
||||
return base64DecodedData;//textChunks[0].text;
|
||||
//console.log(textChunks[0].keyword); // 'hello'
|
||||
//console.log(textChunks[0].text); // 'world'
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok') {
|
||||
try {
|
||||
// Read the image, resize, and save it as a PNG into the buffer
|
||||
|
||||
webp
|
||||
|
||||
const rawImg = await jimp.read(img_url);
|
||||
const image = await rawImg.cover(400, 600).getBufferAsync(jimp.MIME_PNG);
|
||||
|
||||
// Get the chunks
|
||||
const chunks = extract(image);
|
||||
const tEXtChunks = chunks.filter(chunk => chunk.create_date === 'tEXt');
|
||||
|
||||
// Remove all existing tEXt chunks
|
||||
for (let tEXtChunk of tEXtChunks) {
|
||||
chunks.splice(chunks.indexOf(tEXtChunk), 1);
|
||||
}
|
||||
// Add new chunks before the IEND chunk
|
||||
const base64EncodedData = Buffer.from(data, 'utf8').toString('base64');
|
||||
chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData));
|
||||
//chunks.splice(-1, 0, text.encode('lorem', 'ipsum'));
|
||||
|
||||
fs.writeFileSync(target_img, new Buffer.from(encode(chunks)));
|
||||
if (response !== undefined) response.send(mes);
|
||||
return true;
|
||||
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
if (response !== undefined) response.status(500).send(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
(async function() {
|
||||
const spath = process.argv[2]
|
||||
const dpath = process.argv[3] || spath
|
||||
const files = fs.readdirSync(spath).filter(e => e.endsWith(".webp"))
|
||||
if (!files.length) {
|
||||
console.log("Nothing to convert.")
|
||||
return
|
||||
}
|
||||
|
||||
try { fs.mkdirSync(dpath) } catch {}
|
||||
|
||||
for(const f of files) {
|
||||
const source = path.join(spath, f),
|
||||
dest = path.join(dpath, path.basename(f, ".webp") + ".png")
|
||||
|
||||
console.log(`Read... ${source}`)
|
||||
const data = await charaRead(source)
|
||||
|
||||
console.log(`Convert... ${source} -> ${dest}`)
|
||||
await webp.dwebp(source, dest, "-o")
|
||||
|
||||
console.log(`Write... ${dest}`)
|
||||
const success = await charaWrite(dest, data, path.parse(dest).name);
|
||||
|
||||
if (!success) {
|
||||
console.log(`Failure on ${source} -> ${dest}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Remove... ${source}`)
|
||||
fs.rmSync(source)
|
||||
}
|
||||
})()
|
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"exifreader": "^4.12.0",
|
||||
"jimp": "^0.22.7",
|
||||
"png-chunk-text": "^1.0.0",
|
||||
"png-chunks-encode": "^1.0.0",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"webp-converter": "^2.3.3"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user