feat: add charx import
This commit is contained in:
@@ -12,13 +12,13 @@ import { CCardLib, type CharacterCardV3, type LorebookEntry } from '@risuai/ccar
|
||||
import { reencodeImage } from "./process/files/image"
|
||||
import { PngChunk } from "./pngChunk"
|
||||
import type { OnnxModelFiles } from "./process/transformers"
|
||||
import { CharXWriter } from "./process/processzip"
|
||||
import { CharXReader, CharXWriter } from "./process/processzip"
|
||||
|
||||
export const hubURL = "https://sv.risuai.xyz"
|
||||
|
||||
export async function importCharacter() {
|
||||
try {
|
||||
const files = await selectFileByDom(['png', 'json'], 'multiple')
|
||||
const files = await selectFileByDom(["*"], 'multiple')
|
||||
if(!files){
|
||||
return
|
||||
}
|
||||
@@ -63,6 +63,36 @@ async function importCharacterProcess(f:{
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if(f.name.endsWith('charx')){
|
||||
console.log('reading charx')
|
||||
const reader = new CharXReader()
|
||||
alertStore.set({
|
||||
type: 'wait',
|
||||
msg: 'Loading... (Reading)'
|
||||
})
|
||||
await reader.read(f.data)
|
||||
const cardData = reader.cardData
|
||||
if(!cardData){
|
||||
alertError(language.errors.noData)
|
||||
return
|
||||
}
|
||||
const card = JSON.parse(cardData)
|
||||
if(CCardLib.character.check(card) !== 'v3'){
|
||||
alertError(language.errors.noData)
|
||||
return
|
||||
}
|
||||
await importCharacterCardSpec(card, undefined, 'normal', reader.assets)
|
||||
let db = get(DataBase)
|
||||
return db.characters.length - 1
|
||||
}
|
||||
|
||||
if(!f.name.endsWith('png')){
|
||||
alertError(language.errors.noData)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
alertStore.set({
|
||||
type: 'wait',
|
||||
msg: 'Loading... (Reading)'
|
||||
@@ -486,6 +516,13 @@ async function importCharacterCardSpec(card:CharacterCardV2Risu|CharacterCardV3,
|
||||
else if(data.assets[i].uri === 'ccdefault:'){
|
||||
imgp = im
|
||||
}
|
||||
else if(data.assets[i].uri.startsWith('embeded://')){
|
||||
const key = data.assets[i].uri.replace('embeded://', '')
|
||||
imgp = assetDict[key]
|
||||
if(!imgp){
|
||||
throw new Error('Error while importing, asset ' + key + ' not found')
|
||||
}
|
||||
}
|
||||
else{
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AppendableBuffer, type LocalWriter, type VirtualWriter } from "../storage/globalApi";
|
||||
import { AppendableBuffer, saveAsset, type LocalWriter, type VirtualWriter } from "../storage/globalApi";
|
||||
import * as fflate from "fflate";
|
||||
import { sleep } from "../util";
|
||||
|
||||
export async function processZip(dataArray: Uint8Array): Promise<string> {
|
||||
const jszip = await import("jszip");
|
||||
@@ -69,4 +70,74 @@ export class CharXWriter{
|
||||
await this.writer.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CharXReader{
|
||||
unzip:fflate.Unzip
|
||||
assets:{[key:string]:string} = {}
|
||||
assetBuffers:{[key:string]:AppendableBuffer} = {}
|
||||
assetPromises:Promise<void>[] = []
|
||||
excludedFiles:string[] = []
|
||||
cardData:string|undefined
|
||||
constructor(){
|
||||
this.unzip = new fflate.Unzip()
|
||||
this.unzip.register(fflate.UnzipInflate)
|
||||
this.unzip.onfile = (file) => {
|
||||
const assetIndex = file.name
|
||||
this.assetBuffers[assetIndex] = new AppendableBuffer()
|
||||
|
||||
file.ondata = (err, dat, final) => {
|
||||
this.assetBuffers[assetIndex].append(dat)
|
||||
if(final){
|
||||
const assetData = this.assetBuffers[assetIndex].buffer
|
||||
if(assetData.byteLength > 50 * 1024 * 1024){
|
||||
this.excludedFiles.push(assetIndex)
|
||||
}
|
||||
else if(file.name === 'card.json'){
|
||||
this.cardData = new TextDecoder().decode(assetData)
|
||||
}
|
||||
else{
|
||||
this.assetPromises.push((async () => {
|
||||
const assetId = await saveAsset(assetData)
|
||||
this.assets[assetIndex] = assetId
|
||||
})())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(file.originalSize ?? 0 < 50 * 1024 * 1024){
|
||||
file.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async read(data:Uint8Array|File|ReadableStream<Uint8Array>){
|
||||
if(data instanceof Uint8Array){
|
||||
this.unzip.push(data, true)
|
||||
}
|
||||
if(data instanceof File){
|
||||
const reader = data.stream().getReader()
|
||||
while(true){
|
||||
const {done, value} = await reader.read()
|
||||
if(done){
|
||||
break
|
||||
}
|
||||
this.unzip.push(value, false)
|
||||
}
|
||||
this.unzip.push(new Uint8Array(0), true)
|
||||
}
|
||||
if(data instanceof ReadableStream){
|
||||
const reader = data.getReader()
|
||||
while(true){
|
||||
const {done, value} = await reader.read()
|
||||
if(done){
|
||||
break
|
||||
}
|
||||
this.unzip.push(value, false)
|
||||
}
|
||||
this.unzip.push(new Uint8Array(0), true)
|
||||
}
|
||||
await sleep(500)
|
||||
await Promise.all(this.assetPromises)
|
||||
}
|
||||
}
|
||||
@@ -125,12 +125,15 @@ export function selectFileByDom(allowedExtensions:string[], multiple:'multiple'|
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.multiple = multiple === 'multiple';
|
||||
|
||||
if(!(get(DataBase).allowAllExtentionFiles || checkIsIos())){
|
||||
const acceptAll = (get(DataBase).allowAllExtentionFiles || checkIsIos() || allowedExtensions[0] === '*')
|
||||
if(!acceptAll){
|
||||
if (allowedExtensions && allowedExtensions.length) {
|
||||
fileInput.accept = allowedExtensions.map(ext => `.${ext}`).join(',');
|
||||
}
|
||||
}
|
||||
else{
|
||||
fileInput.accept = '*'
|
||||
}
|
||||
|
||||
|
||||
fileInput.addEventListener('change', (event) => {
|
||||
@@ -139,10 +142,10 @@ export function selectFileByDom(allowedExtensions:string[], multiple:'multiple'|
|
||||
return;
|
||||
}
|
||||
|
||||
const files = Array.from(fileInput.files).filter(file => {
|
||||
const files = acceptAll ? Array.from(fileInput.files) :(Array.from(fileInput.files).filter(file => {
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase();
|
||||
return !allowedExtensions || allowedExtensions.includes(fileExtension);
|
||||
});
|
||||
}))
|
||||
|
||||
fileInput.remove()
|
||||
resolve(files);
|
||||
|
||||
Reference in New Issue
Block a user