mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2024-12-14 10:24:47 +01:00
282 lines
11 KiB
JavaScript
282 lines
11 KiB
JavaScript
/*
|
|
TODO:
|
|
*/
|
|
//const DEBUG_TONY_SAMA_FORK_MODE = true
|
|
|
|
import { getRequestHeaders, callPopup } from "../../../script.js";
|
|
import { deleteExtension, extensionNames, installExtension } from "../../extensions.js";
|
|
import { isValidUrl } from "../../utils.js";
|
|
export { MODULE_NAME };
|
|
|
|
const MODULE_NAME = 'Assets';
|
|
const DEBUG_PREFIX = "<Assets module> ";
|
|
let ASSETS_JSON_URL = "https://raw.githubusercontent.com/SillyTavern/SillyTavern-Content/main/index.json"
|
|
|
|
const extensionName = "assets";
|
|
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
|
|
|
// DBG
|
|
//if (DEBUG_TONY_SAMA_FORK_MODE)
|
|
// ASSETS_JSON_URL = "https://raw.githubusercontent.com/Tony-sama/SillyTavern-Content/main/index.json"
|
|
let availableAssets = {};
|
|
let currentAssets = {};
|
|
|
|
//#############################//
|
|
// Extension UI and Settings //
|
|
//#############################//
|
|
|
|
const defaultSettings = {
|
|
}
|
|
|
|
function downloadAssetsList(url) {
|
|
updateCurrentAssets().then(function () {
|
|
fetch(url, { cache: "no-cache" })
|
|
.then(response => response.json())
|
|
.then(json => {
|
|
|
|
availableAssets = {};
|
|
$("#assets_menu").empty();
|
|
|
|
console.debug(DEBUG_PREFIX, "Received assets dictionary", json);
|
|
|
|
for (const i of json) {
|
|
//console.log(DEBUG_PREFIX,i)
|
|
if (availableAssets[i["type"]] === undefined)
|
|
availableAssets[i["type"]] = [];
|
|
|
|
availableAssets[i["type"]].push(i);
|
|
}
|
|
|
|
console.debug(DEBUG_PREFIX, "Updated available assets to", availableAssets);
|
|
// First extensions, then everything else
|
|
const assetTypes = Object.keys(availableAssets).sort((a, b) => (a === 'extension') ? -1 : (b === 'extension') ? 1 : 0);
|
|
|
|
for (const assetType of assetTypes) {
|
|
let assetTypeMenu = $('<div />', { id: "assets_audio_ambient_div", class: "assets-list-div" });
|
|
assetTypeMenu.append(`<h3>${assetType}</h3>`)
|
|
for (const i in availableAssets[assetType]) {
|
|
const asset = availableAssets[assetType][i];
|
|
const elemId = `assets_install_${assetType}_${i}`;
|
|
let element = $('<button />', { id: elemId, type: "button", class: "asset-download-button menu_button" })
|
|
const label = $("<i class=\"fa-solid fa-download fa-xl\"></i>");
|
|
element.append(label);
|
|
|
|
//if (DEBUG_TONY_SAMA_FORK_MODE)
|
|
// asset["url"] = asset["url"].replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG
|
|
|
|
console.debug(DEBUG_PREFIX, "Checking asset", asset["id"], asset["url"]);
|
|
|
|
const assetInstall = async function () {
|
|
element.off("click");
|
|
label.removeClass("fa-download");
|
|
this.classList.add('asset-download-button-loading');
|
|
await installAsset(asset["url"], assetType, asset["id"]);
|
|
label.addClass("fa-check");
|
|
this.classList.remove('asset-download-button-loading');
|
|
element.on("click", assetDelete);
|
|
element.on("mouseenter", function(){
|
|
label.removeClass("fa-check");
|
|
label.addClass("fa-trash");
|
|
label.addClass("redOverlayGlow");
|
|
}).on("mouseleave", function(){
|
|
label.addClass("fa-check");
|
|
label.removeClass("fa-trash");
|
|
label.removeClass("redOverlayGlow");
|
|
});
|
|
};
|
|
|
|
const assetDelete = async function() {
|
|
element.off("click");
|
|
await deleteAsset(assetType, asset["id"]);
|
|
label.removeClass("fa-check");
|
|
label.removeClass("redOverlayGlow");
|
|
label.removeClass("fa-trash");
|
|
label.addClass("fa-download");
|
|
element.off("mouseenter").off("mouseleave");
|
|
element.on("click", assetInstall);
|
|
}
|
|
|
|
if (isAssetInstalled(assetType, asset["id"])) {
|
|
console.debug(DEBUG_PREFIX, "installed, checked");
|
|
label.toggleClass("fa-download");
|
|
label.toggleClass("fa-check");
|
|
element.on("click", assetDelete);
|
|
element.on("mouseenter", function(){
|
|
label.removeClass("fa-check");
|
|
label.addClass("fa-trash");
|
|
label.addClass("redOverlayGlow");
|
|
}).on("mouseleave", function(){
|
|
label.addClass("fa-check");
|
|
label.removeClass("fa-trash");
|
|
label.removeClass("redOverlayGlow");
|
|
});
|
|
}
|
|
else {
|
|
console.debug(DEBUG_PREFIX, "not installed, unchecked")
|
|
element.prop("checked", false);
|
|
element.on("click", assetInstall);
|
|
}
|
|
|
|
console.debug(DEBUG_PREFIX, "Created element for ", asset["id"])
|
|
|
|
const displayName = DOMPurify.sanitize(asset["name"] || asset["id"]);
|
|
const description = DOMPurify.sanitize(asset["description"] || "");
|
|
const url = isValidUrl(asset["url"]) ? asset["url"] : "";
|
|
|
|
$(`<i></i>`)
|
|
.append(element)
|
|
.append(`<div class="flex-container flexFlowColumn">
|
|
<span class="flex-container alignitemscenter">
|
|
<b>${displayName}</b>
|
|
<a href="${url}" target="_blank" title="Open in browser">
|
|
<i class="fa-solid fa-sm fa-arrow-up-right-from-square"></i>
|
|
</a>
|
|
</span>
|
|
<span>${description}</span>
|
|
</div>`)
|
|
.appendTo(assetTypeMenu);
|
|
}
|
|
assetTypeMenu.appendTo("#assets_menu");
|
|
}
|
|
|
|
$("#assets_menu").show();
|
|
})
|
|
.catch((error) => {
|
|
console.error(error);
|
|
toastr.error("Problem with assets URL", DEBUG_PREFIX + "Cannot get assets list");
|
|
$('#assets-connect-button').addClass("fa-plug-circle-exclamation");
|
|
$('#assets-connect-button').addClass("redOverlayGlow");
|
|
});
|
|
});
|
|
}
|
|
|
|
function isAssetInstalled(assetType, filename) {
|
|
let assetList = currentAssets[assetType];
|
|
|
|
if (assetType == 'extension') {
|
|
const thirdPartyMarker = "third-party/";
|
|
assetList = extensionNames.filter(x => x.startsWith(thirdPartyMarker)).map(x => x.replace(thirdPartyMarker, ''));
|
|
}
|
|
|
|
for (const i of assetList) {
|
|
//console.debug(DEBUG_PREFIX,i,filename)
|
|
if (i.includes(filename))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
async function installAsset(url, assetType, filename) {
|
|
console.debug(DEBUG_PREFIX, "Downloading ", url);
|
|
const category = assetType;
|
|
try {
|
|
if (category === 'extension') {
|
|
console.debug(DEBUG_PREFIX, "Installing extension ", url)
|
|
await installExtension(url);
|
|
console.debug(DEBUG_PREFIX, "Extension installed.")
|
|
return;
|
|
}
|
|
|
|
const body = { url, category, filename };
|
|
const result = await fetch('/api/assets/download', {
|
|
method: 'POST',
|
|
headers: getRequestHeaders(),
|
|
body: JSON.stringify(body),
|
|
cache: 'no-cache',
|
|
});
|
|
if (result.ok) {
|
|
console.debug(DEBUG_PREFIX, "Download success.")
|
|
}
|
|
}
|
|
catch (err) {
|
|
console.log(err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function deleteAsset(assetType, filename) {
|
|
console.debug(DEBUG_PREFIX, "Deleting ", assetType, filename);
|
|
const category = assetType;
|
|
try {
|
|
if (category === 'extension') {
|
|
console.debug(DEBUG_PREFIX, "Deleting extension ", filename)
|
|
await deleteExtension(filename);
|
|
console.debug(DEBUG_PREFIX, "Extension deleted.")
|
|
}
|
|
|
|
const body = { category, filename };
|
|
const result = await fetch('/api/assets/delete', {
|
|
method: 'POST',
|
|
headers: getRequestHeaders(),
|
|
body: JSON.stringify(body),
|
|
cache: 'no-cache',
|
|
});
|
|
if (result.ok) {
|
|
console.debug(DEBUG_PREFIX, "Deletion success.")
|
|
}
|
|
}
|
|
catch (err) {
|
|
console.log(err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
//#############################//
|
|
// API Calls //
|
|
//#############################//
|
|
|
|
async function updateCurrentAssets() {
|
|
console.debug(DEBUG_PREFIX, "Checking installed assets...")
|
|
try {
|
|
const result = await fetch(`/api/assets/get`, {
|
|
method: 'POST',
|
|
headers: getRequestHeaders(),
|
|
});
|
|
currentAssets = result.ok ? (await result.json()) : {};
|
|
}
|
|
catch (err) {
|
|
console.log(err);
|
|
}
|
|
console.debug(DEBUG_PREFIX, "Current assets found:", currentAssets)
|
|
}
|
|
|
|
|
|
//#############################//
|
|
// Extension load //
|
|
//#############################//
|
|
|
|
// This function is called when the extension is loaded
|
|
jQuery(async () => {
|
|
// This is an example of loading HTML from a file
|
|
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
|
|
|
const assetsJsonUrl = windowHtml.find('#assets-json-url-field');
|
|
assetsJsonUrl.val(ASSETS_JSON_URL);
|
|
|
|
const connectButton = windowHtml.find('#assets-connect-button');
|
|
connectButton.on("click", async function () {
|
|
const confirmation = await callPopup(`Are you sure you want to connect to '${assetsJsonUrl.val()}'?`, 'confirm')
|
|
if (confirmation) {
|
|
try {
|
|
console.debug(DEBUG_PREFIX, "Confimation, loading assets...");
|
|
downloadAssetsList(assetsJsonUrl.val());
|
|
connectButton.removeClass("fa-plug-circle-exclamation");
|
|
connectButton.removeClass("redOverlayGlow");
|
|
connectButton.addClass("fa-plug-circle-check");
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
toastr.error(`Cannot get assets list from ${assetsJsonUrl.val()}`);
|
|
connectButton.removeClass("fa-plug-circle-check");
|
|
connectButton.addClass("fa-plug-circle-exclamation");
|
|
connectButton.removeClass("redOverlayGlow");
|
|
}
|
|
}
|
|
else {
|
|
console.debug(DEBUG_PREFIX, "Connection refused by user");
|
|
}
|
|
});
|
|
|
|
$('#extensions_settings').append(windowHtml);
|
|
});
|