attempt at metadata grabbing

This commit is contained in:
SoniaNvm
2026-02-02 02:27:09 -08:00
parent d35d704ce1
commit 3bc20ea40d
3 changed files with 876 additions and 872 deletions

View File

@@ -2,4 +2,4 @@
# Disable Discord community banner (optional)
# Uncomment the line below to hide the Discord banner
# NEXT_PUBLIC_DISABLE_DISCORD_BANNER=true
NEXT_PUBLIC_DISABLE_DISCORD_BANNER=true

5
.gitignore vendored
View File

@@ -1,4 +1,9 @@
.idea/
bun.lock
.env
node_modules
.next
.env.local
.env*.local
.env

View File

@@ -89,6 +89,9 @@ export default function Home() {
"https://sucker.severian.dev/api/proxy"
);
const [pageSource, setPageSource] = useState("");
const [metadataJson, setMetadataJson] = useState<any>(null);
const fetchCards = async () => {
try {
setIsRefreshing(true);
@@ -233,6 +236,15 @@ export default function Home() {
const arrayBuffer = await pngBlob.arrayBuffer();
// Fallback if metadataJson hasn't been fetched for this card
const safeMetadata = metadataJson || {};
const creator = (safeMetadata.creator_name || (card.data as any).creator || "Unknown") + (safeMetadata.creator_verified ? " ✅" : "");
// Tag Parsing
const normalTags = (safeMetadata.tags || (card.data as any).tags || []).map((t: any) => t.name);
const customTags = safeMetadata.custom_tags || [];
const allTagsArray = [...normalTags, ...customTags];
// Use initial version for PNG embedding, or current version if no initial version available
const chosen = card.initialVersion || card.data;
const pngData = {
@@ -244,13 +256,13 @@ export default function Home() {
personality: chosen.personality,
mes_example: chosen.mes_example,
scenario: chosen.scenario,
creator: (chosen as any).creator || "",
creator_notes: (chosen as any).creator_notes || "",
creator: creator,
creator_notes: safeMetadata.description || (chosen as any).creator_notes || "",
system_prompt: (chosen as any).system_prompt || "",
post_history_instructions:
(chosen as any).post_history_instructions || "",
tags: (chosen as any).tags || [],
character_version: (chosen as any).character_version || "1",
tags: allTagsArray,
character_version: safeMetadata.name || (chosen as any).character_version || "1",
extensions: (chosen as any).extensions || {},
},
spec: card.spec || "chara_card_v2",
@@ -295,39 +307,9 @@ export default function Home() {
const isCharacterUrl = /janitorai\.com\/characters\//.test(characterUrl);
if (isCharacterUrl) {
// Extract character ID and open metadata page, then show second input
const match = characterUrl.match(/characters\/([\w-]+)/);
if (match && match[1]) {
const characterId = match[1].split("_")[0];
window.open(
`https://janitorai.com/hampter/characters/${characterId}`,
"_blank"
);
// Open html source, then show second input
window.open(`view-source:${characterUrl}`, "_blank");
setIsMetadataOpen(true);
}
return;
}
// Check if the input is a direct image link (webp filename or full image URL)
const isImagePath = /\.(webp|png|jpg|jpeg|gif)(\?.*)?$/i.test(characterUrl);
const isFullImageUrl = (characterUrl.startsWith("http://") || characterUrl.startsWith("https://")) && isImagePath;
const isWebpFilename = /^[\w-]+\.(webp|png|jpg|jpeg|gif)$/i.test(characterUrl);
if (isFullImageUrl || isWebpFilename) {
// Directly set the avatar URL without opening metadata
if (selectedCardIndex === null) return;
const avatarUrl = isFullImageUrl
? characterUrl
: `https://ella.janitorai.com/bot-avatars/${characterUrl}`;
const updatedCards = [...cards];
updatedCards[selectedCardIndex] = {
...updatedCards[selectedCardIndex],
avatarUrl,
};
setCards(updatedCards);
setDialogOpen(false);
return;
}
};
@@ -336,7 +318,42 @@ export default function Home() {
if (selectedCardIndex === null) return;
try {
const avatarUrl = `https://ella.janitorai.com/bot-avatars/${avatarPath}`;
const storeKey = "Sk--a:a-a--characterStore";
// Define the anchor points we are looking for
const prefix = 'window.mbxM.push(JSON.parse("';
const suffix = '"));';
// Locate the script content
const startIndex = pageSource.indexOf(prefix);
if (startIndex === -1) {
throw new Error("Could not find character data in the page source.");
}
// Move index to the start of the actual JSON string
const jsonStartIndex = startIndex + prefix.length;
// Find the end of the statement
const jsonEndIndex = pageSource.indexOf(suffix, jsonStartIndex);
if (jsonEndIndex === -1) {
throw new Error("Could not find closing tag for JSON content.");
}
// Extract the escaped string
const escapedJsonString = pageSource.substring(jsonStartIndex, jsonEndIndex);
// Wrap it in quotes so it gets treated as a string containing JSON
const rawJsonString = JSON.parse(`"${escapedJsonString}"`);
console.log(rawJsonString);
// Parse the actual JSON data into an object
const data = JSON.parse(rawJsonString);
// Return the specific character data
const char = data[storeKey].character;
setMetadataJson(char);
const avatarUrl = `https://ella.janitorai.com/bot-avatars/${char.avatar}`;
const updatedCards = [...cards];
updatedCards[selectedCardIndex] = {
...updatedCards[selectedCardIndex],
@@ -742,7 +759,7 @@ export default function Home() {
onClick={() => handleOpenDialog(index)}
variant="outline"
>
Fetch Avatar (required for PNG)
Fetch Metadata
</Button>
) : (
<Button
@@ -750,7 +767,7 @@ export default function Home() {
variant="default"
disabled={!card.avatarUrl}
>
Download PNG
Download Complete Card
</Button>
)}
</div>
@@ -993,43 +1010,25 @@ export default function Home() {
<DialogContent>
<DialogHeader>
<DialogTitle>
{isMetadataOpen ? "Enter Avatar Path" : "Fetch Avatar"}
{isMetadataOpen ? "Enter Page Source Code" : "Fetch Metadata"}
</DialogTitle>
<DialogDescription>
{isMetadataOpen
? "Look for the avatar field in the opened tab and paste the value here."
: "Enter a character URL (janitorai.com/characters/...) to open metadata, or paste an image filename (id.webp) or full image URL directly."}
{isMetadataOpen ? "Paste the entire source code here." : "Enter a character URL (janitorai.com/characters/...) to open page source."}
</DialogDescription>
</DialogHeader>
{isMetadataOpen ? (
<div className="space-y-4">
<Input
placeholder="id.webp"
value={avatarPath}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setAvatarPath(e.target.value)
}
/>
<Input placeholder="<!DOCTYPE html><html..." value={pageSource} onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPageSource(e.target.value)} />
<Button onClick={handleFetchAvatar} className="w-full">
Fetch Avatar
Fetch Metadata
</Button>
</div>
) : (
<div className="space-y-4">
<Input
placeholder="URL or id.webp"
value={characterUrl}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setCharacterUrl(e.target.value)
}
/>
<p className="text-sm text-muted-foreground">
For character URLs, a new tab will open with metadata. For image
filenames or full image URLs, the avatar will be set directly.
</p>
<Input placeholder="URL" value={characterUrl} onChange={(e: React.ChangeEvent<HTMLInputElement>) => setCharacterUrl(e.target.value)} />
<Button onClick={handleOpenMetadata} className="w-full">
Fetch Avatar
Fetch Metadata
</Button>
</div>
)}