mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Import characters from RisuRealm by URL
This commit is contained in:
@ -1547,6 +1547,13 @@ function getCharacterSource(chId = this_chid) {
|
|||||||
return sourceUrl;
|
return sourceUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const risuId = characters[chId]?.data?.extensions?.risuai?.source;
|
||||||
|
|
||||||
|
if (Array.isArray(risuId) && risuId.length && typeof risuId[0] === 'string' && risuId[0].startsWith('risurealm:')) {
|
||||||
|
const realmId = risuId[0].split(':')[1];
|
||||||
|
return `https://realm.risuai.net/character/${realmId}`;
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5938,7 +5945,7 @@ export function changeMainAPI() {
|
|||||||
getStatusHorde();
|
getStatusHorde();
|
||||||
getHordeModels(true);
|
getHordeModels(true);
|
||||||
}
|
}
|
||||||
validateDisabledSamplers()
|
validateDisabledSamplers();
|
||||||
setupChatCompletionPromptManager(oai_settings);
|
setupChatCompletionPromptManager(oai_settings);
|
||||||
forceCharacterEditorTokenize();
|
forceCharacterEditorTokenize();
|
||||||
}
|
}
|
||||||
@ -6169,7 +6176,7 @@ export async function getSettings() {
|
|||||||
firstRun = false;
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await validateDisabledSamplers()
|
await validateDisabledSamplers();
|
||||||
settingsReady = true;
|
settingsReady = true;
|
||||||
eventSource.emit(event_types.SETTINGS_LOADED);
|
eventSource.emit(event_types.SETTINGS_LOADED);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
* @property {string} [github_repo] - The gitHub repository associated with the character.
|
* @property {string} [github_repo] - The gitHub repository associated with the character.
|
||||||
* @property {string} [source_url] - The source URL associated with the character.
|
* @property {string} [source_url] - The source URL associated with the character.
|
||||||
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
|
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
|
||||||
|
* @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -509,6 +509,39 @@ async function downloadGenericPng(url) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Risu Realm URL to extract the UUID.
|
||||||
|
* @param {string} url Risu Realm URL
|
||||||
|
* @returns {string | null} UUID of the character
|
||||||
|
*/
|
||||||
|
function parseRisuUrl(url) {
|
||||||
|
// Example: https://realm.risuai.net/character/7adb0ed8d81855c820b3506980fb40f054ceef010ff0c4bab73730c0ebe92279
|
||||||
|
const pattern = /^https?:\/\/realm\.risuai\.net\/character\/([a-f0-9]{64})$/;
|
||||||
|
const match = url.match(pattern);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download RisuAI character card
|
||||||
|
* @param {string} uuid UUID of the character
|
||||||
|
* @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>}
|
||||||
|
*/
|
||||||
|
async function downloadRisuCharacter(uuid) {
|
||||||
|
const result = await fetch(`https://realm.risuai.net/api/v1/download/png-v3/${uuid}?non_commercial=true`);
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
const text = await result.text();
|
||||||
|
console.log('RisuAI returned error', result.statusText, text);
|
||||||
|
throw new Error('Failed to download character');
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = await result.buffer();
|
||||||
|
const fileName = `${sanitize(uuid)}.png`;
|
||||||
|
const fileType = 'image/png';
|
||||||
|
|
||||||
|
return { buffer, fileName, fileType };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} url
|
* @param {String} url
|
||||||
* @returns {String | null } UUID of the character
|
* @returns {String | null } UUID of the character
|
||||||
@ -563,6 +596,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
|
|||||||
const isJannnyContent = host.includes('janitorai');
|
const isJannnyContent = host.includes('janitorai');
|
||||||
const isPygmalionContent = host.includes('pygmalion.chat');
|
const isPygmalionContent = host.includes('pygmalion.chat');
|
||||||
const isAICharacterCardsContent = host.includes('aicharactercards.com');
|
const isAICharacterCardsContent = host.includes('aicharactercards.com');
|
||||||
|
const isRisu = host.includes('realm.risuai.net');
|
||||||
const isGeneric = isHostWhitelisted(host);
|
const isGeneric = isHostWhitelisted(host);
|
||||||
|
|
||||||
if (isPygmalionContent) {
|
if (isPygmalionContent) {
|
||||||
@ -603,6 +637,14 @@ router.post('/importURL', jsonParser, async (request, response) => {
|
|||||||
else {
|
else {
|
||||||
return response.sendStatus(404);
|
return response.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
} else if (isRisu) {
|
||||||
|
const uuid = parseRisuUrl(url);
|
||||||
|
if (!uuid) {
|
||||||
|
return response.sendStatus(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
type = 'character';
|
||||||
|
result = await downloadRisuCharacter(uuid);
|
||||||
} else if (isGeneric) {
|
} else if (isGeneric) {
|
||||||
console.log('Downloading from generic url.');
|
console.log('Downloading from generic url.');
|
||||||
type = 'character';
|
type = 'character';
|
||||||
|
Reference in New Issue
Block a user