sillytavern 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,8 +41,6 @@ SillyTavern 本身并无用处,因为它只是一个用户聊天界面。你
41
41
 
42
42
  <https://rentry.org/STAI-Termux>
43
43
 
44
- Termux 不支持**.Webp 字符卡的导入/导出。请使用 JSON 或 PNG 格式**。
45
-
46
44
  ## 有问题或建议?
47
45
 
48
46
  ### 我们现在有了 Discord 社区
package/.github/readme.md CHANGED
@@ -41,8 +41,6 @@ Since Tavern is only a user interface, it has tiny hardware requirements, it wil
41
41
 
42
42
  <https://rentry.org/STAI-Termux>
43
43
 
44
- **.webp character cards import/export is not supported in Termux. Use either JSON or PNG formats instead.**
45
-
46
44
  ## Questions or suggestions?
47
45
 
48
46
  ### We now have a community Discord server
@@ -71,7 +69,6 @@ Get in touch with the developers directly:
71
69
  * [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
72
70
  * [AI Horde](https://horde.koboldai.net/) connection
73
71
  * Prompt generation formatting tweaking
74
- * webp character card interoperability (PNG is still an internal format)
75
72
 
76
73
  ## Extensions
77
74
 
package/package.json CHANGED
@@ -9,7 +9,6 @@
9
9
  "cors": "^2.8.5",
10
10
  "csrf-csrf": "^2.2.3",
11
11
  "device-detector-js": "^3.0.3",
12
- "exifreader": "^4.12.0",
13
12
  "express": "^4.18.2",
14
13
  "google-translate-api-browser": "^3.0.1",
15
14
  "gpt3-tokenizer": "^1.1.5",
@@ -23,7 +22,6 @@
23
22
  "multer": "^1.4.5-lts.1",
24
23
  "node-fetch": "^2.6.11",
25
24
  "open": "^8.4.2",
26
- "piexifjs": "^1.0.6",
27
25
  "png-chunk-text": "^1.0.0",
28
26
  "png-chunks-encode": "^1.0.0",
29
27
  "png-chunks-extract": "^1.0.0",
@@ -31,7 +29,6 @@
31
29
  "sanitize-filename": "^1.6.3",
32
30
  "simple-git": "^3.19.1",
33
31
  "uniqolor": "^1.1.0",
34
- "webp-converter": "2.3.2",
35
32
  "write-file-atomic": "^5.0.1",
36
33
  "ws": "^8.13.0",
37
34
  "yargs": "^17.7.1",
@@ -49,7 +46,7 @@
49
46
  "type": "git",
50
47
  "url": "https://github.com/SillyTavern/SillyTavern.git"
51
48
  },
52
- "version": "1.10.1",
49
+ "version": "1.10.3",
53
50
  "scripts": {
54
51
  "start": "node server.js",
55
52
  "start-multi": "node server.js --disableCsrf",
package/public/i18n.json CHANGED
@@ -284,7 +284,6 @@
284
284
  "Regenerate": "重新生成",
285
285
  "PNG": "PNG",
286
286
  "JSON": "JSON",
287
- "WEBP": "WEBP",
288
287
  "presets": "预设",
289
288
  "Message Sound": "AI 消息提示音",
290
289
  "Author's Note": "作者注释",
@@ -836,7 +835,6 @@
836
835
  "Regenerate": "再生成",
837
836
  "PNG": "PNG",
838
837
  "JSON": "JSON",
839
- "WEBP": "WEBP",
840
838
  "presets": "プリセット",
841
839
  "Message Sound": "メッセージ音",
842
840
  "Author's Note": "作者の注記",
@@ -1392,7 +1390,6 @@
1392
1390
  "Regenerate": "재생성",
1393
1391
  "PNG": "PNG",
1394
1392
  "JSON": "JSON",
1395
- "WEBP": "WEBP",
1396
1393
  "presets": "기본설정",
1397
1394
  "Message Sound": "메시지 효과음",
1398
1395
  "Author's Note": "글쓴이 쪽지",
@@ -2016,7 +2013,6 @@
2016
2013
  "Regenerate": "Повторная генерация",
2017
2014
  "PNG": "PNG",
2018
2015
  "JSON": "JSON",
2019
- "WEBP": "WEBP",
2020
2016
  "presets": "Предустановки",
2021
2017
  "Message Sound": "Звук сообщения",
2022
2018
  "Author's Note": "Авторские заметки",
@@ -2580,7 +2576,6 @@
2580
2576
  "Regenerate": "Rigenera",
2581
2577
  "PNG": "PNG",
2582
2578
  "JSON": "JSON",
2583
- "WEBP": "WEBP",
2584
2579
  "presets": "preset",
2585
2580
  "Message Sound": "Suono del messaggio",
2586
2581
  "Author's Note": "Note d'autore",
@@ -3259,7 +3254,6 @@
3259
3254
  "Regenerate": "Regenereren",
3260
3255
  "PNG": "PNG",
3261
3256
  "JSON": "JSON",
3262
- "WEBP": "WEBP",
3263
3257
  "presets": "sjablonen",
3264
3258
  "Message Sound": "Berichtgeluid",
3265
3259
  "Author's Note": "Notitie van auteur",
package/public/index.html CHANGED
@@ -3482,7 +3482,7 @@
3482
3482
 
3483
3483
  <div id="rm_character_import" class="right_menu" style="display: none;">
3484
3484
  <form id="form_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">
3485
- <input multiple type="file" id="character_import_file" accept=".json, image/png, image/webp" name="avatar">
3485
+ <input multiple type="file" id="character_import_file" accept=".json, image/png" name="avatar">
3486
3486
  <input id="character_import_file_type" name="file_type" class="text_pole" maxlength="999" size="2" value="" autocomplete="off">
3487
3487
  </form>
3488
3488
  </div>
@@ -4445,7 +4445,6 @@
4445
4445
  <div id="export_format_popup" class="list-group">
4446
4446
  <div class="export_format list-group-item" data-format="png">PNG</div>
4447
4447
  <div class="export_format list-group-item" data-format="json">JSON</div>
4448
- <div class="export_format list-group-item" data-format="webp">WEBP</div>
4449
4448
  </div>
4450
4449
 
4451
4450
  <div id="zoomed_avatar_template" class="template_element">
package/public/script.js CHANGED
@@ -5410,17 +5410,18 @@ function select_rm_info(type, charId, previousCharId = null) {
5410
5410
  $('#rm_print_characters_pagination').pagination('go', page);
5411
5411
 
5412
5412
  waitUntilCondition(() => document.querySelector(selector) !== null).then(() => {
5413
- const element = $(selector).parent().get(0);
5413
+ const parent = $('#rm_print_characters_block');
5414
+ const element = $(selector).parent();
5414
5415
 
5415
- if (!element) {
5416
+ if (element.length === 0) {
5416
5417
  console.log(`Could not find element for character ${charId}`);
5417
5418
  return;
5418
5419
  }
5419
5420
 
5420
- element.scrollIntoView({ behavior: 'smooth', block: 'start' });
5421
- $(element).addClass('flash animated');
5421
+ parent.scrollTop(element.position().top + parent.scrollTop());
5422
+ element.addClass('flash animated');
5422
5423
  setTimeout(function () {
5423
- $(element).removeClass('flash animated');
5424
+ element.removeClass('flash animated');
5424
5425
  }, 5000);
5425
5426
  });
5426
5427
  } catch (e) {
@@ -5429,16 +5430,29 @@ function select_rm_info(type, charId, previousCharId = null) {
5429
5430
  }
5430
5431
 
5431
5432
  if (type === 'group_create') {
5432
- //for groups, ${charId} = data.id from group-chats.js createGroup()
5433
- const element = $(`#rm_characters_block [grid="${charId}"]`).get(0);
5434
- element.scrollIntoView({ behavior: 'smooth', block: 'start' });
5433
+ // Find the page at which the character is located
5434
+ const charData = getEntitiesList({ doFilter: true });
5435
+ const charIndex = charData.findIndex((x) => String(x?.item?.id) === String(charId));
5436
+
5437
+ if (charIndex === -1) {
5438
+ console.log(`Could not find group ${charId} in the list`);
5439
+ return;
5440
+ }
5441
+
5442
+ const perPage = Number(localStorage.getItem('Characters_PerPage'));
5443
+ const page = Math.floor(charIndex / perPage) + 1;
5444
+ $('#rm_print_characters_pagination').pagination('go', page);
5445
+ const parent = $('#rm_print_characters_block');
5446
+ const selector = `#rm_print_characters_block [grid="${charId}"]`;
5435
5447
  try {
5436
- if (element !== undefined || element !== null) {
5448
+ waitUntilCondition(() => document.querySelector(selector) !== null).then(() => {
5449
+ const element = $(selector);
5450
+ parent.scrollTop(element.position().top + parent.scrollTop());
5437
5451
  $(element).addClass('flash animated');
5438
5452
  setTimeout(function () {
5439
5453
  $(element).removeClass('flash animated');
5440
5454
  }, 5000);
5441
- } else { console.log('didnt find the element'); }
5455
+ });
5442
5456
  } catch (e) {
5443
5457
  console.error(e);
5444
5458
  }
@@ -6732,7 +6746,6 @@ export function processDroppedFiles(files) {
6732
6746
  const allowedMimeTypes = [
6733
6747
  'application/json',
6734
6748
  'image/png',
6735
- 'image/webp',
6736
6749
  ];
6737
6750
 
6738
6751
  for (const file of files) {
@@ -6748,7 +6761,7 @@ function importCharacter(file) {
6748
6761
  const ext = file.name.match(/\.(\w+)$/);
6749
6762
  if (
6750
6763
  !ext ||
6751
- (ext[1].toLowerCase() != "json" && ext[1].toLowerCase() != "png" && ext[1] != "webp")
6764
+ (ext[1].toLowerCase() != "json" && ext[1].toLowerCase() != "png")
6752
6765
  ) {
6753
6766
  return;
6754
6767
  }
@@ -582,10 +582,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
582
582
  typingIndicator
583
583
  .find(".typing_indicator_name")
584
584
  .text(characters[chId].name);
585
- $("#chat").append(typingIndicator);
586
- typingIndicator.show(200, function () {
587
- typingIndicator.get(0).scrollIntoView({ behavior: "smooth" });
588
- });
585
+ typingIndicator.show();
589
586
  }
590
587
 
591
588
  // TODO: This is awful. Refactor this
@@ -681,9 +678,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
681
678
  }
682
679
  }
683
680
  } finally {
684
- // hide and reapply the indicator to the bottom of the list
685
- typingIndicator.hide(200);
686
- $("#chat").append(typingIndicator);
681
+ typingIndicator.hide();
687
682
 
688
683
  is_group_generating = false;
689
684
  $("#send_textarea").attr("disabled", false);
@@ -1109,6 +1104,7 @@ function select_group_chats(groupId, skipAnimation) {
1109
1104
  $("#rm_group_restore_avatar").toggle(!!group && isValidImageUrl(group.avatar_url));
1110
1105
  $("#rm_group_filter").val("").trigger("input");
1111
1106
  $(`input[name="rm_group_activation_strategy"][value="${replyStrategy}"]`).prop('checked', true);
1107
+ $("#rm_group_chat_name").val(groupName);
1112
1108
 
1113
1109
  if (!skipAnimation) {
1114
1110
  selectRightMenuWithAnimation('rm_group_chats_block');
@@ -576,6 +576,7 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
576
576
  chatCompletion.add(collection, index);
577
577
  };
578
578
 
579
+ chatCompletion.reserveBudget(3); // every reply is primed with <|start|>assistant<|message|>
579
580
  // Character and world information
580
581
  addToChatCompletion('worldInfoBefore');
581
582
  addToChatCompletion('main');
@@ -1761,9 +1762,12 @@ class ChatCompletion {
1761
1762
  /**
1762
1763
  * Reserves the tokens required by the given message from the token budget.
1763
1764
  *
1764
- * @param {Message|MessageCollection} message - The message whose tokens to reserve.
1765
+ * @param {Message|MessageCollection|number} message - The message whose tokens to reserve.
1765
1766
  */
1766
- reserveBudget(message) { this.decreaseTokenBudgetBy(message.getTokens()) };
1767
+ reserveBudget(message) {
1768
+ const tokens = typeof message === 'number' ? message : message.getTokens();
1769
+ this.decreaseTokenBudgetBy(tokens);
1770
+ };
1767
1771
 
1768
1772
  /**
1769
1773
  * Frees up the tokens used by the given message from the token budget.
package/public/style.css CHANGED
@@ -2768,6 +2768,7 @@ body .ui-widget-content li:hover {
2768
2768
  margin: 10px;
2769
2769
  opacity: 0.85;
2770
2770
  text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor);
2771
+ order: 9999;
2771
2772
  }
2772
2773
 
2773
2774
  .typing_indicator:after {
@@ -3608,4 +3609,4 @@ a {
3608
3609
  height: 100vh;
3609
3610
  z-index: 9999;
3610
3611
  }
3611
- }
3612
+ }
package/server.js CHANGED
@@ -32,6 +32,8 @@ const multer = require("multer");
32
32
  const responseTime = require('response-time');
33
33
 
34
34
  // net related library imports
35
+ const net = require("net");
36
+ const dns = require('dns');
35
37
  const DeviceDetector = require("device-detector-js");
36
38
  const fetch = require('node-fetch').default;
37
39
  const ipaddr = require('ipaddr.js');
@@ -40,13 +42,11 @@ const json5 = require('json5');
40
42
  const WebSocket = require('ws');
41
43
 
42
44
  // image processing related library imports
43
- const exif = require('piexifjs');
44
45
  const encode = require('png-chunks-encode');
45
46
  const extract = require('png-chunks-extract');
46
47
  const jimp = require('jimp');
47
48
  const mime = require('mime-types');
48
49
  const PNGtext = require('png-chunk-text');
49
- const webp = require('webp-converter');
50
50
  const yauzl = require('yauzl');
51
51
 
52
52
  // tokenizing related library imports
@@ -89,9 +89,16 @@ function createDefaultFiles() {
89
89
  }
90
90
  }
91
91
 
92
- const net = require("net");
93
- // @ts-ignore work around a node v20 bug: https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
94
- if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
92
+ // Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0.
93
+ // https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
94
+ // Safe to remove once support for Node v20 is dropped.
95
+ if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) {
96
+ // @ts-ignore
97
+ if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false);
98
+ }
99
+
100
+ // Set default DNS resolution order to IPv4 first
101
+ dns.setDefaultResultOrder('ipv4first');
95
102
 
96
103
  const cliArguments = yargs(hideBin(process.argv))
97
104
  .option('disableCsrf', {
@@ -1128,7 +1135,7 @@ app.post("/renamecharacter", jsonParser, async function (request, response) {
1128
1135
  try {
1129
1136
  // Read old file, replace name int it
1130
1137
  const rawOldData = await charaRead(oldAvatarPath);
1131
- if (rawOldData === false || rawOldData === undefined) throw new Error("Failed to read character file");
1138
+ if (rawOldData === undefined) throw new Error("Failed to read character file");
1132
1139
 
1133
1140
  const oldData = getCharaCardV2(json5.parse(rawOldData));
1134
1141
  _.set(oldData, 'data.name', newName);
@@ -1375,7 +1382,7 @@ const calculateDataSize = (data) => {
1375
1382
  const processCharacter = async (item, i) => {
1376
1383
  try {
1377
1384
  const img_data = await charaRead(charactersPath + item);
1378
- if (img_data === false || img_data === undefined) throw new Error("Failed to read character file");
1385
+ if (img_data === undefined) throw new Error("Failed to read character file");
1379
1386
 
1380
1387
  let jsonObject = getCharaCardV2(json5.parse(img_data));
1381
1388
  jsonObject.avatar = item;
@@ -2212,26 +2219,13 @@ app.post("/importcharacter", urlencodedParser, async function (request, response
2212
2219
  } else {
2213
2220
  try {
2214
2221
  var img_data = await charaRead(uploadPath, format);
2215
- if (img_data === false || img_data === undefined) throw new Error('Failed to read character data');
2222
+ if (img_data === undefined) throw new Error('Failed to read character data');
2216
2223
 
2217
2224
  let jsonData = json5.parse(img_data);
2218
2225
 
2219
2226
  jsonData.name = sanitize(jsonData.data?.name || jsonData.name);
2220
2227
  png_name = getPngName(jsonData.name);
2221
2228
 
2222
- if (format == 'webp') {
2223
- try {
2224
- let convertedPath = path.join(UPLOADS_PATH, path.basename(uploadPath, ".webp") + ".png")
2225
- await webp.dwebp(uploadPath, convertedPath, "-o");
2226
- fs.unlinkSync(uploadPath);
2227
- uploadPath = convertedPath;
2228
- }
2229
- catch {
2230
- console.error('WEBP image conversion failed. Using the default character image.');
2231
- uploadPath = defaultAvatarPath;
2232
- }
2233
- }
2234
-
2235
2229
  if (jsonData.spec !== undefined) {
2236
2230
  console.log('Found a v2 character file.');
2237
2231
  importRisuSprites(jsonData);
@@ -2411,7 +2405,7 @@ app.post("/exportcharacter", jsonParser, async function (request, response) {
2411
2405
  case 'json': {
2412
2406
  try {
2413
2407
  let json = await charaRead(filename);
2414
- if (json === false || json === undefined) return response.sendStatus(400);
2408
+ if (json === undefined) return response.sendStatus(400);
2415
2409
  let jsonObject = getCharaCardV2(json5.parse(json));
2416
2410
  return response.type('json').send(jsonObject)
2417
2411
  }
@@ -2419,39 +2413,6 @@ app.post("/exportcharacter", jsonParser, async function (request, response) {
2419
2413
  return response.sendStatus(400);
2420
2414
  }
2421
2415
  }
2422
- case 'webp': {
2423
- try {
2424
- let json = await charaRead(filename);
2425
- if (json === false || json === undefined) return response.sendStatus(400);
2426
- let stringByteArray = utf8Encode.encode(json).toString();
2427
- let inputWebpPath = path.join(UPLOADS_PATH, `${Date.now()}_input.webp`);
2428
- let outputWebpPath = path.join(UPLOADS_PATH, `${Date.now()}_output.webp`);
2429
- let metadataPath = path.join(UPLOADS_PATH, `${Date.now()}_metadata.exif`);
2430
- let metadata =
2431
- {
2432
- "Exif": {
2433
- [exif.ExifIFD.UserComment]: stringByteArray,
2434
- },
2435
- };
2436
- const exifString = exif.dump(metadata);
2437
- writeFileAtomicSync(metadataPath, exifString, 'binary');
2438
-
2439
- await webp.cwebp(filename, inputWebpPath, '-q 95');
2440
- await webp.webpmux_add(inputWebpPath, outputWebpPath, metadataPath, 'exif');
2441
-
2442
- response.sendFile(outputWebpPath, { root: process.cwd() }, () => {
2443
- fs.rmSync(inputWebpPath);
2444
- fs.rmSync(metadataPath);
2445
- fs.rmSync(outputWebpPath);
2446
- });
2447
-
2448
- return;
2449
- }
2450
- catch (err) {
2451
- console.log(err);
2452
- return response.sendStatus(400);
2453
- }
2454
- }
2455
2416
  }
2456
2417
 
2457
2418
  return response.sendStatus(400);
@@ -4005,8 +3966,10 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
4005
3966
  headers: { "Content-Type": "application/json" }
4006
3967
  };
4007
3968
 
4008
- if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
4009
- args.headers = Object.assign(args.headers, get_mancer_headers());
3969
+ if (main_api == 'textgenerationwebui') {
3970
+ if (request.body.use_mancer) {
3971
+ args.headers = Object.assign(args.headers, get_mancer_headers());
3972
+ }
4010
3973
  const data = await postAsync(api_server + "/v1/token-count", args);
4011
3974
  return response.send({ count: data['results'][0]['tokens'] });
4012
3975
  }
@@ -4078,8 +4041,6 @@ const setupTasks = async function () {
4078
4041
  contentManager.checkForNewContent();
4079
4042
  cleanUploads();
4080
4043
 
4081
- await convertWebp();
4082
-
4083
4044
  [spp_llama, spp_nerd, spp_nerd_v2, claude_tokenizer] = await Promise.all([
4084
4045
  loadSentencepieceTokenizer('src/sentencepiece/tokenizer.model'),
4085
4046
  loadSentencepieceTokenizer('src/sentencepiece/nerdstash.model'),
@@ -4139,47 +4100,6 @@ if (true === cliArguments.ssl) {
4139
4100
  );
4140
4101
  }
4141
4102
 
4142
- async function convertWebp() {
4143
- const files = fs.readdirSync(directories.characters).filter(e => e.endsWith(".webp"));
4144
-
4145
- if (!files.length) {
4146
- return;
4147
- }
4148
-
4149
- console.log(`${files.length} WEBP files will be automatically converted.`);
4150
-
4151
- for (const file of files) {
4152
- try {
4153
- const source = path.join(directories.characters, file);
4154
- const dest = path.join(directories.characters, path.basename(file, ".webp") + ".png");
4155
-
4156
- if (fs.existsSync(dest)) {
4157
- console.log(`${dest} already exists. Delete ${source} manually`);
4158
- continue;
4159
- }
4160
-
4161
- console.log(`Read... ${source}`);
4162
- const data = await charaRead(source);
4163
-
4164
- console.log(`Convert... ${source} -> ${dest}`);
4165
- await webp.dwebp(source, dest, "-o");
4166
-
4167
- console.log(`Write... ${dest}`);
4168
- const success = await charaWrite(dest, data, path.parse(dest).name);
4169
-
4170
- if (!success) {
4171
- console.log(`Failure on ${source} -> ${dest}`);
4172
- continue;
4173
- }
4174
-
4175
- console.log(`Remove... ${source}`);
4176
- fs.rmSync(source);
4177
- } catch (err) {
4178
- console.log(err);
4179
- }
4180
- }
4181
- }
4182
-
4183
4103
  function backupSettings() {
4184
4104
  const MAX_BACKUPS = 25;
4185
4105
 
@@ -1,56 +1,12 @@
1
1
  const fs = require('fs');
2
- const json5 = require('json5');
3
- const ExifReader = require('exifreader');
4
2
 
5
3
  const extract = require('png-chunks-extract');
6
4
  const PNGtext = require('png-chunk-text');
7
5
 
8
- const utf8Decode = new TextDecoder('utf-8', { ignoreBOM: true });
9
-
10
6
  const parse = async (cardUrl, format) => {
11
- let fileFormat;
12
- if (format === undefined) {
13
- if (cardUrl.indexOf('.webp') !== -1)
14
- fileFormat = 'webp';
15
- else
16
- fileFormat = 'png';
17
- }
18
- else
19
- fileFormat = format;
7
+ let fileFormat = format === undefined ? 'png' : format;
20
8
 
21
9
  switch (fileFormat) {
22
- case 'webp':
23
- try {
24
- const exif_data = await ExifReader.load(fs.readFileSync(cardUrl));
25
- let char_data;
26
-
27
- if (exif_data['UserComment']['description']) {
28
- let description = exif_data['UserComment']['description'];
29
- if (description === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) {
30
- description = exif_data['UserComment'].value[0];
31
- }
32
-
33
- try {
34
- json5.parse(description);
35
- char_data = description;
36
- } catch {
37
- const byteArr = description.split(",").map(Number);
38
- const uint8Array = new Uint8Array(byteArr);
39
- const char_data_string = utf8Decode.decode(uint8Array);
40
- char_data = char_data_string;
41
- }
42
- }
43
- else {
44
- console.log('No description found in EXIF data.');
45
- return false;
46
- }
47
-
48
- return char_data;
49
- }
50
- catch (err) {
51
- console.log(err);
52
- return false;
53
- }
54
10
  case 'png':
55
11
  const buffer = fs.readFileSync(cardUrl);
56
12
  const chunks = extract(buffer);
package/Launcher.bat DELETED
@@ -1,641 +0,0 @@
1
- @echo off
2
- REM --------------------------------------------
3
- REM This script was created by: Deffcolony
4
- REM --------------------------------------------
5
- title SillyTavern Launcher
6
- setlocal
7
-
8
- REM ANSI Escape Code for Colors
9
- set "reset="
10
-
11
- REM Strong Foreground Colors
12
- set "white_fg_strong="
13
- set "red_fg_strong="
14
- set "green_fg_strong="
15
- set "yellow_fg_strong="
16
- set "blue_fg_strong="
17
- set "magenta_fg_strong="
18
- set "cyan_fg_strong="
19
-
20
- REM Normal Background Colors
21
- set "red_bg="
22
- set "blue_bg="
23
-
24
- REM Environment Variables (TOOLBOX 7-Zip)
25
- set "zip7version=7z2301-x64"
26
- set "zip7_install_path=%ProgramFiles%\7-Zip"
27
- set "zip7_download_path=%TEMP%\%zip7version%.exe"
28
-
29
- REM Environment Variables (TOOLBOX FFmpeg)
30
- set "ffmpeg_url=https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z"
31
- set "ffdownload_path=%TEMP%\ffmpeg.7z"
32
- set "ffextract_path=C:\ffmpeg"
33
- set "bin_path=%ffextract_path%\bin"
34
-
35
- REM Environment Variables (TOOLBOX Node.js)
36
- set "node_installer_path=%temp%\NodejsInstaller.msi"
37
-
38
- REM Environment Variables (winget)
39
- set "winget_path=%userprofile%\AppData\Local\Microsoft\WindowsApps"
40
-
41
- REM Environment Variables (TOOLBOX Install Extras)
42
- set "miniconda_path=%userprofile%\miniconda"
43
-
44
-
45
- REM Check if Winget is installed; if not, then install it
46
- winget --version > nul 2>&1
47
- if %errorlevel% neq 0 (
48
- echo %yellow_fg_strong%[WARN] Winget is not installed on this system.
49
- echo %blue_fg_strong%[INFO]%reset% Installing Winget...
50
- 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"
51
- start "" "%temp%\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
52
- echo %green_fg_strong%Winget is now installed.%reset%
53
- ) else (
54
- echo %blue_fg_strong%[INFO] Winget is already installed.%reset%
55
- )
56
-
57
- rem Get the current PATH value from the registry
58
- for /f "tokens=2*" %%A in ('reg query "HKCU\Environment" /v PATH') do set "current_path=%%B"
59
-
60
- rem Check if the paths are already in the current PATH
61
- echo %current_path% | find /i "%winget_path%" > nul
62
- set "ff_path_exists=%errorlevel%"
63
-
64
- rem Append the new paths to the current PATH only if they don't exist
65
- if %ff_path_exists% neq 0 (
66
- set "new_path=%current_path%;%winget_path%"
67
-
68
- rem Update the PATH value in the registry
69
- reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%new_path%" /f
70
-
71
- rem Update the PATH value for the current session
72
- setx PATH "%new_path%" > nul
73
- echo %green_fg_strong%winget added to PATH.%reset%
74
- ) else (
75
- set "new_path=%current_path%"
76
- echo %blue_fg_strong%[INFO] winget already exists in PATH.%reset%
77
- )
78
-
79
-
80
- REM Check if Git is installed if not then install git
81
- git --version > nul 2>&1
82
- if %errorlevel% neq 0 (
83
- echo %yellow_fg_strong%[WARN] Git is not installed on this system.%reset%
84
- echo %blue_fg_strong%[INFO]%reset% Installing Git using Winget...
85
- winget install -e --id Git.Git
86
- echo %green_fg_strong%Git is installed. Please restart the Launcher.%reset%
87
- pause
88
- exit
89
- ) else (
90
- echo %blue_fg_strong%[INFO] Git is already installed.%reset%
91
- )
92
-
93
- REM Check for updates
94
- git fetch origin
95
-
96
- for /f %%i in ('git rev-list HEAD...origin/%current_branch%') do (
97
- set "update_status=%yellow_fg_strong%Update Available%reset%"
98
- goto :found_update
99
- )
100
-
101
- set "update_status=%green_fg_strong%Up to Date%reset%"
102
- :found_update
103
-
104
-
105
- REM Home - frontend
106
- :home
107
- cls
108
- echo %blue_fg_strong%/ Home%reset%
109
- echo -------------------------------------
110
- echo What would you like to do?
111
- echo 1. Start SillyTavern
112
- echo 2. Start SillyTavern + Extras
113
- echo 3. Update
114
- echo 4. Backup
115
- echo 5. Switch branch
116
- echo 6. Toolbox
117
- echo 7. Exit
118
-
119
- REM Get the current Git branch
120
- for /f %%i in ('git branch --show-current') do set current_branch=%%i
121
- echo ======== VERSION STATUS =========
122
- echo SillyTavern branch: %cyan_fg_strong%%current_branch%%reset%
123
- echo Update Status: %update_status%
124
- echo =================================
125
-
126
- set "choice="
127
- set /p "choice=Choose Your Destiny (default is 1): "
128
-
129
- REM Default to choice 1 if no input is provided
130
- if not defined choice set "choice=1"
131
-
132
- REM Home - backend
133
- if "%choice%"=="1" (
134
- call :start
135
- ) else if "%choice%"=="2" (
136
- call :start_extras
137
- ) else if "%choice%"=="3" (
138
- call :update
139
- ) else if "%choice%"=="4" (
140
- call :backup_menu
141
- ) else if "%choice%"=="5" (
142
- call :switchbrance_menu
143
- ) else if "%choice%"=="6" (
144
- call :toolbox
145
- ) else if "%choice%"=="7" (
146
- exit
147
- ) else (
148
- color 6
149
- echo WARNING: Invalid number. Please insert a valid number.
150
- pause
151
- goto :home
152
- )
153
-
154
-
155
- :start
156
- REM Check if Node.js is installed
157
- node --version > nul 2>&1
158
- if %errorlevel% neq 0 (
159
- echo %red_fg_strong%[ERROR] node command not found in PATH%reset%
160
- echo %red_bg%Please make sure Node.js is installed and added to your PATH.%reset%
161
- echo %blue_bg%To install Node.js go to Toolbox%reset%
162
- pause
163
- goto :home
164
- )
165
- echo %blue_fg_strong%[INFO]%reset% A new window has been launched.
166
- start /wait cmd /c start.bat
167
- goto :home
168
-
169
-
170
- :start_extras
171
- REM Run conda activate from the Miniconda installation
172
- call "%miniconda_path%\Scripts\activate.bat"
173
-
174
- REM Activate the sillytavernextras environment
175
- call conda activate sillytavernextras
176
-
177
- REM Start SillyTavern Extras with desired configurations
178
- 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
179
- goto :home
180
-
181
-
182
- :update
183
- echo Updating...
184
- pushd %~dp0
185
- REM Check if git is installed
186
- git --version > nul 2>&1
187
- if %errorlevel% neq 0 (
188
- echo %red_fg_strong%[ERROR] git command not found in PATH. Skipping update.%reset%
189
- echo %red_bg%Please make sure Git is installed and added to your PATH.%reset%
190
- echo %blue_bg%To install Git go to Toolbox%reset%
191
- ) else (
192
- call git pull --rebase --autostash
193
- if %errorlevel% neq 0 (
194
- REM incase there is still something wrong
195
- echo There were errors while updating. Please download the latest version manually.
196
- )
197
- )
198
- pause
199
- goto :home
200
-
201
-
202
- REM Switch Brance - frontend
203
- :switchbrance_menu
204
- cls
205
- echo %blue_fg_strong%/ Home / Switch Branch%reset%
206
- echo -------------------------------------
207
- echo What would you like to do?
208
- echo 1. Switch to Release - SillyTavern
209
- echo 2. Switch to Staging - SillyTavern
210
- echo 3. Switch to Main - Extras
211
- echo 4. Switch to Neo - Extras
212
- echo 5. Back to Home
213
-
214
- REM Get the current Git branch
215
- for /f %%i in ('git branch --show-current') do set current_branch=%%i
216
- echo ======== VERSION STATUS =========
217
- echo SillyTavern branch: %cyan_fg_strong%%current_branch%%reset%
218
- echo Extras branch: %cyan_fg_strong%%current_branch%%reset%
219
- echo =================================
220
- set /p brance_choice=Choose Your Destiny:
221
-
222
- REM Switch Brance - backend
223
- if "%brance_choice%"=="1" (
224
- call :switch_release_st
225
- ) else if "%brance_choice%"=="2" (
226
- call :switch_staging_st
227
- ) else if "%brance_choice%"=="3" (
228
- call :switch_main_ste
229
- ) else if "%brance_choice%"=="4" (
230
- call :switch_neo_ste
231
- ) else if "%brance_choice%"=="5" (
232
- goto :home
233
- ) else (
234
- color 6
235
- echo WARNING: Invalid number. Please insert a valid number.
236
- pause
237
- goto :switchbrance_menu
238
- )
239
-
240
-
241
- :switch_release_st
242
- echo %blue_fg_strong%[INFO]%reset% Switching to release branch...
243
- git switch release
244
- pause
245
- goto :switchbrance_menu
246
-
247
-
248
- :switch_staging_st
249
- echo %blue_fg_strong%[INFO]%reset% Switching to staging branch...
250
- git switch staging
251
- pause
252
- goto :switchbrance_menu
253
-
254
-
255
- :switch_main_ste
256
- echo %blue_fg_strong%[INFO]%reset% Switching to main branch...
257
- cd SillyTavern-extras
258
- git switch main
259
- pause
260
- goto :switchbrance_menu
261
-
262
-
263
- :switch_neo_ste
264
- echo %blue_fg_strong%[INFO]%reset% Switching to neo branch...
265
- cd SillyTavern-extras
266
- git switch neo
267
- pause
268
- goto :switchbrance_menu
269
-
270
-
271
- REM Backup - Frontend
272
- :backup_menu
273
- REM Check if 7-Zip is installed
274
- 7z > nul 2>&1
275
- if %errorlevel% neq 0 (
276
- echo %red_fg_strong%[ERROR] 7z command not found in PATH%reset%
277
- echo %red_bg%Please make sure 7-Zip is installed and added to your PATH.%reset%
278
- echo %blue_bg%To install 7-Zip go to Toolbox%reset%
279
- pause
280
- goto :home
281
- )
282
- cls
283
- echo %blue_fg_strong%/ Home / Backup%reset%
284
- echo -------------------------------------
285
- echo What would you like to do?
286
- REM color 7
287
- echo 1. Create Backup
288
- echo 2. Restore Backup
289
- echo 3. Back to Home
290
-
291
- set /p backup_choice=Choose Your Destiny:
292
-
293
- REM Backup - Backend
294
- if "%backup_choice%"=="1" (
295
- call :create_backup
296
- ) else if "%backup_choice%"=="2" (
297
- call :restore_backup
298
- ) else if "%backup_choice%"=="3" (
299
- goto :home
300
- ) else (
301
- color 6
302
- echo WARNING: Invalid number. Please insert a valid number.
303
- pause
304
- goto :backup_menu
305
- )
306
-
307
- :create_backup
308
- REM Create a backup using 7zip
309
- 7z a "backups\backup_.7z" ^
310
- "public\assets\*" ^
311
- "public\Backgrounds\*" ^
312
- "public\Characters\*" ^
313
- "public\Chats\*" ^
314
- "public\context\*" ^
315
- "public\Group chats\*" ^
316
- "public\Groups\*" ^
317
- "public\instruct\*" ^
318
- "public\KoboldAI Settings\*" ^
319
- "public\movingUI\*" ^
320
- "public\NovelAI Settings\*" ^
321
- "public\OpenAI Settings\*" ^
322
- "public\QuickReplies\*" ^
323
- "public\TextGen Settings\*" ^
324
- "public\themes\*" ^
325
- "public\User Avatars\*" ^
326
- "public\user\*" ^
327
- "public\worlds\*" ^
328
- "public\settings.json" ^
329
- "secrets.json"
330
-
331
- REM Get current date and time components
332
- for /f "tokens=1-3 delims=/- " %%d in ("%date%") do (
333
- set "day=%%d"
334
- set "month=%%e"
335
- set "year=%%f"
336
- )
337
-
338
- for /f "tokens=1-2 delims=:." %%h in ("%time%") do (
339
- set "hour=%%h"
340
- set "minute=%%i"
341
- )
342
-
343
- REM Pad single digits with leading zeros
344
- setlocal enabledelayedexpansion
345
- set "day=0!day!"
346
- set "month=0!month!"
347
- set "hour=0!hour!"
348
- set "minute=0!minute!"
349
-
350
- set "formatted_date=%month:~-2%-%day:~-2%-%year%_%hour:~-2%%minute:~-2%"
351
-
352
- REM Rename the backup file with the formatted date and time
353
- rename "backups\backup_.7z" "backup_%formatted_date%.7z"
354
-
355
- endlocal
356
-
357
-
358
- echo %green_fg_strong%Backup created successfully!%reset%
359
- pause
360
- endlocal
361
- goto :backup_menu
362
-
363
-
364
- :restore_backup
365
- REM Restore a backup using 7zip
366
-
367
- echo List of available backups:
368
- echo =========================
369
-
370
- setlocal enabledelayedexpansion
371
- set "backup_count=0"
372
-
373
- for %%F in ("backups\backup_*.7z") do (
374
- set /a "backup_count+=1"
375
- set "backup_files[!backup_count!]=%%~nF"
376
- echo !backup_count!. %cyan_fg_strong%%%~nF%reset%
377
- )
378
-
379
- echo =========================
380
- set /p "restore_choice=Enter number of backup to restore: "
381
-
382
- if "%restore_choice%" geq "1" (
383
- if "%restore_choice%" leq "%backup_count%" (
384
- set "selected_backup=!backup_files[%restore_choice%]!"
385
- echo Restoring backup !selected_backup!...
386
- REM Extract the contents of the "public" folder directly into the existing "public" folder
387
- 7z x "backups\!selected_backup!.7z" -o"temp" -aoa
388
- xcopy /y /e "temp\public\*" "public\"
389
- rmdir /s /q "temp"
390
- echo %green_fg_strong%!selected_backup! restored successfully.%reset%
391
- ) else (
392
- color 6
393
- echo WARNING: Invalid backup number. Please insert a valid number.
394
- )
395
- ) else (
396
- color 6
397
- echo WARNING: Invalid number. Please insert a valid number.
398
- )
399
- pause
400
- goto :backup_menu
401
-
402
-
403
- REM Toolbox - Frontend
404
- :toolbox
405
- cls
406
- echo %blue_fg_strong%/ Home / Toolbox%reset%
407
- echo -------------------------------------
408
- echo What would you like to do?
409
- REM color 7
410
- echo 1. Install 7-Zip
411
- echo 2. Install FFmpeg
412
- echo 3. Install Node.js
413
- echo 4. Edit Environment
414
- echo 5. Reinstall SillyTavern
415
- echo 6. Reinstall Extras
416
- echo 7. Back to Home
417
-
418
- set /p toolbox_choice=Choose Your Destiny:
419
-
420
- REM Toolbox - Backend
421
- if "%toolbox_choice%"=="1" (
422
- call :install7zip
423
- ) else if "%toolbox_choice%"=="2" (
424
- call :installffmpeg
425
- ) else if "%toolbox_choice%"=="3" (
426
- call :installnodejs
427
- ) else if "%toolbox_choice%"=="4" (
428
- call :editenvironment
429
- ) else if "%toolbox_choice%"=="5" (
430
- call :reinstallsillytavern
431
- ) else if "%toolbox_choice%"=="6" (
432
- call :reinstallextras
433
- ) else if "%toolbox_choice%"=="7" (
434
- goto :home
435
- ) else (
436
- color 6
437
- echo WARNING: Invalid number. Please insert a valid number.
438
- pause
439
- goto :toolbox
440
- )
441
-
442
-
443
- :install7zip
444
- echo %blue_fg_strong%[INFO]%reset% Installing 7-Zip...
445
- winget install -e --id 7zip.7zip
446
-
447
- rem Get the current PATH value from the registry
448
- for /f "tokens=2*" %%A in ('reg query "HKCU\Environment" /v PATH') do set "current_path=%%B"
449
-
450
- rem Check if the paths are already in the current PATH
451
- echo %current_path% | find /i "%zip7_install_path%" > nul
452
- set "zip7_path_exists=%errorlevel%"
453
-
454
- rem Append the new paths to the current PATH only if they don't exist
455
- if %zip7_path_exists% neq 0 (
456
- set "new_path=%current_path%;%zip7_install_path%"
457
- echo %green_fg_strong%7-Zip added to PATH.%reset%
458
- ) else (
459
- set "new_path=%current_path%"
460
- echo %blue_fg_strong%[INFO] 7-Zip already exists in PATH.%reset%
461
- )
462
-
463
- rem Update the PATH value in the registry
464
- reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%new_path%" /f
465
-
466
- rem Update the PATH value for the current session
467
- setx PATH "%new_path%"
468
-
469
- echo %green_fg_strong%7-Zip is installed. Please restart the Launcher.%reset%
470
- pause
471
- exit
472
-
473
-
474
- :installffmpeg
475
- REM Check if 7-Zip is installed
476
- 7z > nul 2>&1
477
- if %errorlevel% neq 0 (
478
- echo %red_fg_strong%[ERROR] 7z command not found in PATH%reset%
479
- echo %red_bg%Please make sure 7-Zip is installed and added to your PATH.%reset%
480
- echo %blue_bg%To install 7-Zip go to Toolbox%reset%
481
- pause
482
- goto :toolbox
483
- )
484
-
485
- echo %blue_fg_strong%[INFO]%reset% Downloading FFmpeg archive...
486
- rem bitsadmin /transfer "ffmpeg" /download /priority FOREGROUND "%ffmpeg_url%" "%ffdownload_path%"
487
- curl -o "%ffdownload_path%" "%ffmpeg_url%"
488
-
489
- echo %blue_fg_strong%[INFO]%reset% Creating ffmpeg directory if it doesn't exist...
490
- if not exist "%ffextract_path%" (
491
- mkdir "%ffextract_path%"
492
- )
493
-
494
- echo %blue_fg_strong%[INFO]%reset% Extracting FFmpeg archive...
495
- 7z x "%ffdownload_path%" -o"%ffextract_path%"
496
-
497
-
498
- echo %blue_fg_strong%[INFO]%reset% Moving FFmpeg contents to C:\ffmpeg...
499
- for /d %%i in ("%ffextract_path%\ffmpeg-*-full_build") do (
500
- xcopy "%%i\bin" "%ffextract_path%\bin" /E /I /Y
501
- xcopy "%%i\doc" "%ffextract_path%\doc" /E /I /Y
502
- xcopy "%%i\presets" "%ffextract_path%\presets" /E /I /Y
503
- rd "%%i" /S /Q
504
- )
505
-
506
- rem Get the current PATH value from the registry
507
- for /f "tokens=2*" %%A in ('reg query "HKCU\Environment" /v PATH') do set "current_path=%%B"
508
-
509
- rem Check if the paths are already in the current PATH
510
- echo %current_path% | find /i "%bin_path%" > nul
511
- set "ff_path_exists=%errorlevel%"
512
-
513
- rem Append the new paths to the current PATH only if they don't exist
514
- if %ff_path_exists% neq 0 (
515
- set "new_path=%current_path%;%bin_path%"
516
- echo %green_fg_strong%ffmpeg added to PATH.%reset%
517
- ) else (
518
- set "new_path=%current_path%"
519
- echo %blue_fg_strong%[INFO] ffmpeg already exists in PATH.%reset%
520
- )
521
-
522
- rem Update the PATH value in the registry
523
- reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%new_path%" /f
524
-
525
- rem Update the PATH value for the current session
526
- setx PATH "%new_path%" > nul
527
-
528
- del "%ffdownload_path%"
529
- echo %green_fg_strong%FFmpeg is installed. Please restart the Launcher.%reset%
530
- pause
531
- exit
532
-
533
-
534
- :installnodejs
535
- echo %blue_fg_strong%[INFO]%reset% Installing Node.js...
536
- winget install -e --id OpenJS.NodeJS
537
- echo %green_fg_strong%Node.js is installed. Please restart the Launcher.%reset%
538
- pause
539
- exit
540
-
541
- :editenvironment
542
- rundll32.exe sysdm.cpl,EditEnvironmentVariables
543
- goto :toolbox
544
-
545
- :reinstallsillytavern
546
- setlocal enabledelayedexpansion
547
- chcp 65001 > nul
548
- REM Define the names of items to be excluded
549
- set "script_name=%~nx0"
550
- set "excluded_folders=backups"
551
- set "excluded_files=!script_name!"
552
-
553
- REM Confirm with the user before proceeding
554
- echo.
555
- echo %red_bg%╔════ DANGER ZONE ══════════════════════════════════════════════════════════════════════════════╗%reset%
556
- echo %red_bg%║ WARNING: This will delete all data in the current branch except the Backups. ║%reset%
557
- echo %red_bg%║ If you want to keep any data, make sure to create a backup before proceeding. ║%reset%
558
- echo %red_bg%╚═══════════════════════════════════════════════════════════════════════════════════════════════╝%reset%
559
- echo.
560
- echo Are you sure you want to proceed? [Y/N]
561
- set /p "confirmation="
562
- if /i "!confirmation!"=="Y" (
563
- REM Remove non-excluded folders
564
- for /d %%D in (*) do (
565
- set "exclude_folder="
566
- for %%E in (!excluded_folders!) do (
567
- if "%%D"=="%%E" set "exclude_folder=true"
568
- )
569
- if not defined exclude_folder (
570
- rmdir /s /q "%%D" 2>nul
571
- )
572
- )
573
-
574
- REM Remove non-excluded files
575
- for %%F in (*) do (
576
- set "exclude_file="
577
- for %%E in (!excluded_files!) do (
578
- if "%%F"=="%%E" set "exclude_file=true"
579
- )
580
- if not defined exclude_file (
581
- del /f /q "%%F" 2>nul
582
- )
583
- )
584
-
585
- REM Clone repo into %temp% folder
586
- git clone https://github.com/SillyTavern/SillyTavern.git "%temp%\SillyTavernTemp"
587
-
588
- REM Move the contents of the temporary folder to the current directory
589
- xcopy /e /y "%temp%\SillyTavernTemp\*" .
590
-
591
- REM Clean up the temporary folder
592
- rmdir /s /q "%temp%\SillyTavernTemp"
593
-
594
- echo %green_fg_strong%SillyTavern reinstalled successfully!%reset%
595
- ) else (
596
- echo Reinstall canceled.
597
- )
598
- endlocal
599
- pause
600
- goto :toolbox
601
-
602
-
603
- :reinstallextras
604
- cls
605
- echo %blue_fg_strong%SillyTavern Extras%reset%
606
- echo ---------------------------------------------------------------
607
- echo %blue_fg_strong%[INFO]%reset% Installing SillyTavern Extras...
608
- echo --------------------------------
609
- echo %cyan_fg_strong%This may take a while. Please be patient.%reset%
610
-
611
- winget install -e --id Anaconda.Miniconda3
612
-
613
- REM Run conda activate from the Miniconda installation
614
- call "%miniconda_path%\Scripts\activate.bat"
615
-
616
- REM Create a Conda environment named sillytavernextras
617
- call conda create -n sillytavernextras -y
618
-
619
- REM Activate the sillytavernextras environment
620
- call conda activate sillytavernextras
621
-
622
- REM Install Python 3.11 and Git in the sillytavernextras environment
623
- call conda install python=3.11 git -y
624
-
625
- REM Clone the SillyTavern Extras repository
626
- git clone https://github.com/SillyTavern/SillyTavern-extras
627
-
628
- REM Navigate to the SillyTavern-extras directory
629
- cd SillyTavern-extras
630
-
631
- REM Install Python dependencies from requirements files
632
- pip install -r requirements-complete.txt
633
- pip install -r requirements-rvc.txt
634
-
635
- REM Start SillyTavern Extras with desired configurations
636
- 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
637
-
638
- echo.
639
- echo %green_fg_strong%SillyTavern Extras have been successfully installed.%reset%
640
- pause
641
- goto :toolbox
@@ -1,117 +0,0 @@
1
- import fs from 'fs';
2
- import jimp from 'jimp';
3
- import extract from 'png-chunks-extract';
4
- import encode from 'png-chunks-encode';
5
- import PNGtext from 'png-chunk-text';
6
- import ExifReader from 'exifreader';
7
- import webp from 'webp-converter';
8
- import path from 'path';
9
-
10
- async function charaRead(img_url, input_format){
11
- let format;
12
- if(input_format === undefined){
13
- if(img_url.indexOf('.webp') !== -1){
14
- format = 'webp';
15
- }else{
16
- format = 'png';
17
- }
18
- }else{
19
- format = input_format;
20
- }
21
-
22
- switch(format){
23
- case 'webp':
24
- const exif_data = await ExifReader.load(fs.readFileSync(img_url));
25
- const char_data = exif_data['UserComment']['description'];
26
- if (char_data === 'Undefined' && exif_data['UserComment'].value && exif_data['UserComment'].value.length === 1) {
27
- return exif_data['UserComment'].value[0];
28
- }
29
- return char_data;
30
- case 'png':
31
- const buffer = fs.readFileSync(img_url);
32
- const chunks = extract(buffer);
33
-
34
- const textChunks = chunks.filter(function (chunk) {
35
- return chunk.name === 'tEXt';
36
- }).map(function (chunk) {
37
- //console.log(text.decode(chunk.data));
38
- return PNGtext.decode(chunk.data);
39
- });
40
- var base64DecodedData = Buffer.from(textChunks[0].text, 'base64').toString('utf8');
41
- return base64DecodedData;//textChunks[0].text;
42
- //console.log(textChunks[0].keyword); // 'hello'
43
- //console.log(textChunks[0].text); // 'world'
44
- default:
45
- break;
46
- }
47
-
48
- }
49
-
50
- async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok') {
51
- try {
52
- // Read the image, resize, and save it as a PNG into the buffer
53
-
54
- webp
55
-
56
- const rawImg = await jimp.read(img_url);
57
- const image = await rawImg.cover(400, 600).getBufferAsync(jimp.MIME_PNG);
58
-
59
- // Get the chunks
60
- const chunks = extract(image);
61
- const tEXtChunks = chunks.filter(chunk => chunk.create_date === 'tEXt');
62
-
63
- // Remove all existing tEXt chunks
64
- for (let tEXtChunk of tEXtChunks) {
65
- chunks.splice(chunks.indexOf(tEXtChunk), 1);
66
- }
67
- // Add new chunks before the IEND chunk
68
- const base64EncodedData = Buffer.from(data, 'utf8').toString('base64');
69
- chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData));
70
- //chunks.splice(-1, 0, text.encode('lorem', 'ipsum'));
71
-
72
- fs.writeFileSync(target_img, new Buffer.from(encode(chunks)));
73
- if (response !== undefined) response.send(mes);
74
- return true;
75
-
76
-
77
- } catch (err) {
78
- console.log(err);
79
- if (response !== undefined) response.status(500).send(err);
80
- return false;
81
- }
82
- }
83
-
84
-
85
- (async function() {
86
- const spath = process.argv[2]
87
- const dpath = process.argv[3] || spath
88
- const files = fs.readdirSync(spath).filter(e => e.endsWith(".webp"))
89
- if (!files.length) {
90
- console.log("Nothing to convert.")
91
- return
92
- }
93
-
94
- try { fs.mkdirSync(dpath) } catch {}
95
-
96
- for(const f of files) {
97
- const source = path.join(spath, f),
98
- dest = path.join(dpath, path.basename(f, ".webp") + ".png")
99
-
100
- console.log(`Read... ${source}`)
101
- const data = await charaRead(source)
102
-
103
- console.log(`Convert... ${source} -> ${dest}`)
104
- await webp.dwebp(source, dest, "-o")
105
-
106
- console.log(`Write... ${dest}`)
107
- const success = await charaWrite(dest, data, path.parse(dest).name);
108
-
109
- if (!success) {
110
- console.log(`Failure on ${source} -> ${dest}`);
111
- continue;
112
- }
113
-
114
- console.log(`Remove... ${source}`)
115
- fs.rmSync(source)
116
- }
117
- })()
@@ -1,10 +0,0 @@
1
- {
2
- "dependencies": {
3
- "exifreader": "^4.12.0",
4
- "jimp": "^0.22.7",
5
- "png-chunk-text": "^1.0.0",
6
- "png-chunks-encode": "^1.0.0",
7
- "png-chunks-extract": "^1.0.0",
8
- "webp-converter": "^2.3.3"
9
- }
10
- }